* commit '13d31b0a40dd58d61e8a6ad82c1de165792b95e4': docs: Making TV Apps Searchable - new training
This commit is contained in:
@@ -32,26 +32,26 @@ Android uses the filename as the resource ID.</dd>
|
||||
<pre class="stx">
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<<a href="#searchable-element">searchable</a> xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:label="<em>string resource</em>"
|
||||
android:hint="<em>string resource</em>"
|
||||
android:searchMode=["queryRewriteFromData" | "queryRewriteFromText"]
|
||||
android:searchButtonText="<em>string resource</em>"
|
||||
android:inputType="<em>{@link android.R.attr#inputType}</em>"
|
||||
android:imeOptions="<em>{@link android.R.attr#imeOptions}</em>"
|
||||
android:searchSuggestAuthority="<em>string</em>"
|
||||
android:searchSuggestPath="<em>string</em>"
|
||||
android:searchSuggestSelection="<em>string</em>"
|
||||
android:searchSuggestIntentAction="<em>string</em>"
|
||||
android:searchSuggestIntentData="<em>string</em>"
|
||||
android:searchSuggestThreshold="<em>int</em>"
|
||||
android:includeInGlobalSearch=["true" | "false"]
|
||||
android:searchSettingsDescription="<em>string resource</em>"
|
||||
android:queryAfterZeroResults=["true" | "false"]
|
||||
android:voiceSearchMode=["showVoiceSearchButton" | "launchWebSearch" | "launchRecognizer"]
|
||||
android:voiceLanguageModel=["free-form" | "web_search"]
|
||||
android:voicePromptText="<em>string resource</em>"
|
||||
android:voiceLanguage="<em>string</em>"
|
||||
android:voiceMaxResults="<em>int</em>"
|
||||
android:<a href="#label">label</a>="<em>string resource</em>"
|
||||
android:<a href="#hint">hint</a>="<em>string resource</em>"
|
||||
android:<a href="#searchMode">searchMode</a>=["queryRewriteFromData" | "queryRewriteFromText"]
|
||||
android:<a href="#searchButtonText">searchButtonText</a>="<em>string resource</em>"
|
||||
android:<a href="#inputType">inputType</a>="<em>{@link android.R.attr#inputType}</em>"
|
||||
android:<a href="#imeOptions">imeOptions</a>="<em>{@link android.R.attr#imeOptions}</em>"
|
||||
android:<a href="#searchSuggestAuthority">searchSuggestAuthority</a>="<em>string</em>"
|
||||
android:<a href="#searchSuggestPath">searchSuggestPath</a>="<em>string</em>"
|
||||
android:<a href="#searchSuggestSelection">searchSuggestSelection</a>="<em>string</em>"
|
||||
android:<a href="#searchSuggestIntentAction">searchSuggestIntentAction</a>="<em>string</em>"
|
||||
android:<a href="#searchSuggestIntentData">searchSuggestIntentData</a>="<em>string</em>"
|
||||
android:<a href="#searchSuggestThreshold">searchSuggestThreshold</a>="<em>int</em>"
|
||||
android:<a href="#includeInGlobalSearch">includeInGlobalSearch</a>=["true" | "false"]
|
||||
android:<a href="#searchSettingsDescription">searchSettingsDescription</a>="<em>string resource</em>"
|
||||
android:<a href="#queryAfterZeroResults">queryAfterZeroResults</a>=["true" | "false"]
|
||||
android:<a href="#voiceSearchMode">voiceSearchMode</a>=["showVoiceSearchButton" | "launchWebSearch" | "launchRecognizer"]
|
||||
android:<a href="#voiceLanguageModel">voiceLanguageModel</a>=["free-form" | "web_search"]
|
||||
android:<a href="#voicePromptText">voicePromptText</a>="<em>string resource</em>"
|
||||
android:<a href="#voiceLanguage">voiceLanguage</a>="<em>string</em>"
|
||||
android:<a href="#voiceMaxResults">voiceMaxResults</a>="<em>int</em>"
|
||||
>
|
||||
<<a href="#actionkey-element">actionkey</a>
|
||||
android:keycode="<em>{@link android.view.KeyEvent KEYCODE}</em>"
|
||||
@@ -69,7 +69,7 @@ Android uses the filename as the resource ID.</dd>
|
||||
<dd>Defines all search configurations used by the Android system to provide assisted search.
|
||||
<p class="caps">attributes:</p>
|
||||
<dl class="atn-list">
|
||||
<dt><code>android:label</code></dt>
|
||||
<dt><a name="label"></a><code>android:label</code></dt>
|
||||
<dd><em>String resource</em>. (Required.) The name of your application.
|
||||
It should be the same as the name applied to the {@code android:label} attribute of your <a
|
||||
href="{@docRoot}guide/topics/manifest/activity-element.html#label">{@code <activity>}</a> or
|
||||
@@ -78,14 +78,14 @@ href="{@docRoot}guide/topics/manifest/activity-element.html#label">{@code <ac
|
||||
<code>android:includeInGlobalSearch</code> to "true", in which case, this label is used to identify
|
||||
your application as a searchable item in the system's search settings.</dd>
|
||||
|
||||
<dt><code>android:hint</code></dt>
|
||||
<dt><a name="hint"></a><code>android:hint</code></dt>
|
||||
<dd><em>String resource</em>. (Recommended.) The text to display in the search text field when
|
||||
no text has been entered. It provides a hint to the user about what
|
||||
content is searchable. For consistency with other Android applications, you should format the
|
||||
string for {@code android:hint} as "Search <em><content-or-product></em>". For example,
|
||||
"Search songs and artists" or "Search YouTube".</dd>
|
||||
|
||||
<dt><code>android:searchMode</code></dt>
|
||||
<dt><a name="searchMode"></a><code>android:searchMode</code></dt>
|
||||
<dd><em>Keyword</em>. Sets additional modes that control the search presentation.
|
||||
Currently available modes define how the query text should be rewritten when a custom suggestion
|
||||
receives focus. The following mode values are accepted:
|
||||
@@ -109,19 +109,19 @@ inspection and editing, typically HTTP URI's.</td>
|
||||
href="adding-custom-suggestions.html#RewritingQueryText">Adding Custom Suggestions</a>.</p>
|
||||
</dd>
|
||||
|
||||
<dt><code>android:searchButtonText</code></dt>
|
||||
<dt><a name="searchButtonText"></a><code>android:searchButtonText</code></dt>
|
||||
<dd><em>String resource</em>. The text to display in the button that executes search. By
|
||||
default, the button shows a search icon (a magnifying glass), which is ideal for
|
||||
internationalization, so you should not use this attribute to change the button unless the
|
||||
behavior is something other than a search (such as a URL request in a web browser).</dd>
|
||||
|
||||
<dt><code>android:inputType</code></dt>
|
||||
<dt><a name="inputType"></a><code>android:inputType</code></dt>
|
||||
<dd><em>Keyword</em>. Defines the type of input method (such as the type of soft keyboard)
|
||||
to use. For most searches, in which free-form text is expected, you don't
|
||||
to use. For most searches, in which free-form text is expected, you don't
|
||||
need this attribute. See {@link android.R.attr#inputType} for a list of suitable values for this
|
||||
attribute.</dd>
|
||||
|
||||
<dt><code>android:imeOptions</code></dt>
|
||||
<dt><a name="imeOptions"></a><code>android:imeOptions</code></dt>
|
||||
<dd><em>Keyword</em>. Supplies additional options for the input method.
|
||||
For most searches, in which free-form text is expected, you don't need this attribute. The
|
||||
default IME is "actionSearch" (provides the "search" button instead of a carriage
|
||||
@@ -139,12 +139,12 @@ for this attribute.
|
||||
{@code <searchable>} attributes:</p><br/>
|
||||
|
||||
<dl class="atn-list">
|
||||
<dt><code>android:searchSuggestAuthority</code></dt>
|
||||
<dt><a name="searchSuggestAuthority"></a><code>android:searchSuggestAuthority</code></dt>
|
||||
<dd><em>String</em>. (Required to provide search suggestions.)
|
||||
This value must match the authority string provided in the {@code android:authorities}
|
||||
attribute of the Android manifest {@code <provider>} element.</dd>
|
||||
|
||||
<dt><code>android:searchSuggestPath</code></dt>
|
||||
<dt><a name="searchSuggestPath"></a><code>android:searchSuggestPath</code></dt>
|
||||
<dd><em>String</em>. This path is used as a portion of the suggestions
|
||||
query {@link android.net.Uri}, after the prefix and authority, but before
|
||||
the standard suggestions path.
|
||||
@@ -152,7 +152,7 @@ the standard suggestions path.
|
||||
of suggestions (such as for different data types) and you need
|
||||
a way to disambiguate the suggestions queries when you receive them.</dd>
|
||||
|
||||
<dt><code>android:searchSuggestSelection</code></dt>
|
||||
<dt><a name="searchSuggestSelection"></a><code>android:searchSuggestSelection</code></dt>
|
||||
<dd><em>String</em>. This value is passed into your
|
||||
query function as the {@code selection} parameter. Typically this is a WHERE clause
|
||||
for your database, and should contain a single question mark, which is a placeholder for the
|
||||
@@ -160,22 +160,22 @@ actual query string that has been typed by the user (for example, {@code "query=
|
||||
can also use any non-null value to trigger the delivery of the query text via the {@code
|
||||
selectionArgs} parameter (and then ignore the {@code selection} parameter).</dd>
|
||||
|
||||
<dt><code>android:searchSuggestIntentAction</code></dt>
|
||||
<dt><a name="searchSuggestIntentAction"></a><code>android:searchSuggestIntentAction</code></dt>
|
||||
<dd><em>String</em>. The default intent action to be used when a user
|
||||
clicks on a custom search suggestion (such as {@code "android.intent.action.VIEW"}).
|
||||
If this is not overridden by the selected suggestion (via the {@link
|
||||
android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column), this value is placed in the action
|
||||
field of the {@link android.content.Intent} when the user clicks a suggestion.</dd>
|
||||
|
||||
<dt><code>android:searchSuggestIntentData</code></dt>
|
||||
<dt><a name="searchSuggestIntentData"></a><code>android:searchSuggestIntentData</code></dt>
|
||||
<dd><em>String</em>. The default intent data to be used when a user
|
||||
clicks on a custom search suggestion.
|
||||
If not overridden by the selected suggestion (via the {@link
|
||||
android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column), this value is
|
||||
placed in the data field of the {@link android.content.Intent} when the user clicks
|
||||
placed in the data field of the {@link android.content.Intent} when the user clicks
|
||||
a suggestion.</dd>
|
||||
|
||||
<dt><code>android:searchSuggestThreshold</code></dt>
|
||||
<dt><a name="searchSuggestThreshold"></a><code>android:searchSuggestThreshold</code></dt>
|
||||
<dd><em>Integer</em>. The minimum number of characters needed to
|
||||
trigger a suggestion look-up. Only guarantees that the system will not query your
|
||||
content provider for anything shorter than the threshold. The default value is 0.</dd>
|
||||
@@ -192,20 +192,20 @@ android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column), this value is
|
||||
following {@code <searchable>} attributes:</p><br/>
|
||||
|
||||
<dl class="atn-list">
|
||||
<dt><code>android:includeInGlobalSearch</code></dt>
|
||||
<dt><a name="includeInGlobalSearch"></a><code>android:includeInGlobalSearch</code></dt>
|
||||
<dd><em>Boolean</em>. (Required to provide search suggestions in
|
||||
Quick Search Box.) Set to "true" if you want your suggestions to be
|
||||
included in the globally accessible Quick Search Box. The user must
|
||||
still enable your application as a searchable item in the system search settings before
|
||||
your suggestions will appear in Quick Search Box.</dd>
|
||||
|
||||
<dt><code>android:searchSettingsDescription</code></dt>
|
||||
<dt><a name="searchSettingsDescription"></a><code>android:searchSettingsDescription</code></dt>
|
||||
<dd><em>String</em>. Provides a brief description of the search suggestions that you provide
|
||||
to Quick Search Box, which is displayed in the searchable items entry for your application.
|
||||
Your description should concisely describe the content that is searchable. For example, "Artists,
|
||||
albums, and tracks" for a music application, or "Saved notes" for a notepad application.</dd>
|
||||
|
||||
<dt><code>android:queryAfterZeroResults</code></dt>
|
||||
<dt><a name="queryAfterZeroResults"></a><code>android:queryAfterZeroResults</code></dt>
|
||||
<dd><em>Boolean</em>. Set to "true" if you want your content provider to be invoked for
|
||||
supersets of queries that have returned zero results in the past. For example, if
|
||||
your content provider returned zero results for "bo", it should be requiried for "bob". If set to
|
||||
@@ -222,7 +222,7 @@ content provider again). The default value is false.</dd>
|
||||
following {@code <searchable>} attributes:</p><br/>
|
||||
|
||||
<dl class="atn-list">
|
||||
<dt><code>android:voiceSearchMode</code></dt>
|
||||
<dt><a name="voiceSearchMode"></a><code>android:voiceSearchMode</code></dt>
|
||||
<dd><em>Keyword</em>. (Required to provide voice search capabilities.)
|
||||
Enables voice search, with a specific mode for voice search.
|
||||
(Voice search may not be provided by the device, in which case these flags
|
||||
@@ -252,7 +252,7 @@ then either {@code "launchWebSearch"} or {@code "launchRecognizer"} must also be
|
||||
</table>
|
||||
</dd>
|
||||
|
||||
<dt><code>android:voiceLanguageModel</code></dt>
|
||||
<dt><a name="voiceLanguageModel"></a><code>android:voiceLanguageModel</code></dt>
|
||||
<dd><em>Keyword</em>. The language model that
|
||||
should be used by the voice recognition system. The following values are accepted:
|
||||
<table>
|
||||
@@ -268,20 +268,20 @@ optimized for English. This is the default.</td>
|
||||
available in more languages than "free_form".</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>Also see
|
||||
<p>Also see
|
||||
{@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL} for more
|
||||
information.</p></dd>
|
||||
|
||||
<dt><code>android:voicePromptText</code></dt>
|
||||
<dt><a name="voicePromptText"></a><code>android:voicePromptText</code></dt>
|
||||
<dd><em>String</em>. An additional message to display in the voice input dialog.</dd>
|
||||
|
||||
<dt><code>android:voiceLanguage</code></dt>
|
||||
<dt><a name="voiceLanguage"></a><code>android:voiceLanguage</code></dt>
|
||||
<dd><em>String</em>. The spoken language to be expected, expressed as the string value of
|
||||
a constants in {@link java.util.Locale} (such as {@code "de"} for German or {@code "fr"} for
|
||||
French). This is needed only if it is different from the current value of {@link
|
||||
java.util.Locale#getDefault() Locale.getDefault()}.</dd>
|
||||
|
||||
<dt><code>android:voiceMaxResults</code></dt>
|
||||
<dt><a name="voiceMaxResults"></a><code>android:voiceMaxResults</code></dt>
|
||||
<dd><em>Integer</em>. Forces the maximum number of results to return,
|
||||
including the "best" result which is always provided as the {@link
|
||||
android.content.Intent#ACTION_SEARCH} intent's primary
|
||||
@@ -308,7 +308,7 @@ keys are not generally discoverable, so you should not provide them as a core us
|
||||
other three attributes in order to define the search action.</p>
|
||||
<p class="caps">attributes:</p>
|
||||
<dl class="atn-list">
|
||||
<dt><code>android:keycode</code></dt>
|
||||
<dt><a name="keycode"></a><code>android:keycode</code></dt>
|
||||
<dd><em>String</em>. (Required.) A key code from {@link
|
||||
android.view.KeyEvent} that represents the action key
|
||||
you wish to respond to (for example {@code "KEYCODE_CALL"}). This is added to the
|
||||
@@ -318,7 +318,7 @@ android.view.KeyEvent} that represents the action key
|
||||
keys are supported for a search action, as many of them are used for typing, navigation, or system
|
||||
functions.</dd>
|
||||
|
||||
<dt><code>android:queryActionMsg</code></dt>
|
||||
<dt><a name="queryActionMsg"></a><code>android:queryActionMsg</code></dt>
|
||||
<dd><em>String</em>. An action message to be sent if the action key is pressed while the
|
||||
user is entering query text. This is added to the
|
||||
{@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that the system
|
||||
@@ -326,17 +326,17 @@ user is entering query text. This is added to the
|
||||
{@link android.content.Intent#getStringExtra
|
||||
getStringExtra(SearchManager.ACTION_MSG)}.</dd>
|
||||
|
||||
<dt><code>android:suggestActionMsg</code></dt>
|
||||
<dt><a name="suggestActionMsg"></a><code>android:suggestActionMsg</code></dt>
|
||||
<dd><em>String</em>. An action message to be sent if the action key is pressed while a
|
||||
suggestion is in focus. This is added to the
|
||||
intent that that the system passes to your searchable activity (using the action
|
||||
you've defined for the suggestion). To examine the string,
|
||||
use {@link android.content.Intent#getStringExtra
|
||||
use {@link android.content.Intent#getStringExtra
|
||||
getStringExtra(SearchManager.ACTION_MSG)}. This should only be used if all your
|
||||
suggestions support this action key. If not all suggestions can handle the same action key, then
|
||||
you must instead use the following {@code android:suggestActionMsgColumn} attribute.</dd>
|
||||
|
||||
<dt><code>android:suggestActionMsgColumn</code></dt>
|
||||
<dt><a name="suggestActionMsgColumn"></a><code>android:suggestActionMsgColumn</code></dt>
|
||||
<dd><em>String</em>. The name of the column in your content provider that defines the
|
||||
action message for this action key, which is to be sent if the user presses the action key while a
|
||||
suggestion is in focus. This attribute lets you control the
|
||||
|
||||
@@ -948,6 +948,9 @@ include the action bar on devices running Android 2.1 or higher."
|
||||
<a href="<?cs var:toroot ?>training/tv/discovery/recommendations.html">
|
||||
Recommending TV Content</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="<?cs var:toroot ?>training/tv/discovery/searchable.html">
|
||||
Making TV Apps Searchable</a>
|
||||
<li>
|
||||
<a href="<?cs var:toroot ?>training/tv/discovery/in-app-search.html">
|
||||
Searching within TV Apps</a>
|
||||
|
||||
383
docs/html/training/tv/discovery/searchable.jd
Normal file
383
docs/html/training/tv/discovery/searchable.jd
Normal file
@@ -0,0 +1,383 @@
|
||||
page.title=Making TV Apps Searchable
|
||||
page.tags="search","searchable"
|
||||
|
||||
trainingnavtop=true
|
||||
|
||||
@jd:body
|
||||
|
||||
<div id="tb-wrapper">
|
||||
<div id="tb">
|
||||
<h2>This lesson teaches you to</h2>
|
||||
<ol>
|
||||
<li><a href="#columns">Identify Columns</a></li>
|
||||
<li><a href="#provide">Provide Search Suggestion Data</a></li>
|
||||
<li><a href="#suggestions">Handle Search Suggestions</a></li>
|
||||
<li><a href="#terms">Handle Search Terms</a></li>
|
||||
<li><a href="#details">Deep Link to Your App in the Details Screen</a></li>
|
||||
</ol>
|
||||
<h2>You should also read</h2>
|
||||
<ul>
|
||||
<li><a href="{@docRoot}guide/topics/search/index.html">Search</a></li>
|
||||
<li><a href="{@docRoot}training/search/index.html">Adding Search Functionality</a></li>
|
||||
</ul>
|
||||
<h2>Try it out</h2>
|
||||
<ul>
|
||||
<li><a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android Leanback sample app</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>Android TV uses the Android <a href="{@docRoot}guide/topics/search/index.html">search interface</a>
|
||||
to retrieve content data from installed apps and deliver search results to the user. Your app's
|
||||
content data can be included with these results, to give the user instant access to the content in
|
||||
your app.</p>
|
||||
|
||||
<p>Your app must provide Android TV with the data fields from which it generates suggested search
|
||||
results as the user enters characters in the search dialog. To do that, your app must implement a
|
||||
<a href="{@docRoot}guide/topics/providers/content-providers.html">Content Provider</a> that serves
|
||||
up the suggestions along with a <a href="{@docRoot}guide/topics/search/searchable-config.html">
|
||||
{@code searchable.xml}</a> configuration file that describes the content
|
||||
provider and other vital information for Android TV. You also need an activity that handles the
|
||||
intent that fires when the user selects a suggested search result. All of this is described in
|
||||
more detail in <a href="{@docRoot}guide/topics/search/adding-custom-suggestions.html">Adding Custom
|
||||
Suggestions</a>. Here are described the main points for Android TV apps.</p>
|
||||
|
||||
<p>This lesson builds on your knowledge of using search in Android to show you how to make your app
|
||||
searchable in Android TV. Be sure you are familiar with the concepts explained in the
|
||||
<a href="{@docRoot}guide/topics/search/index.html">Search API guide</a> before following this lesson.
|
||||
See also the training <a href="{@docRoot}training/search/index.html">Adding Search Functionality</a>.</p>
|
||||
|
||||
<p>This discussion describes some code from the
|
||||
<a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android Leanback sample app</a>,
|
||||
available on GitHub.</p>
|
||||
|
||||
<h2 id="columns">Identify Columns</h2>
|
||||
|
||||
<p>The {@link android.app.SearchManager} describes the data fields it expects by representing them as
|
||||
columns of an SQLite database. Regardless of your data's format, you must map your data fields to
|
||||
these columns, usually in the class that accessess your content data. For information about building
|
||||
a class that maps your existing data to the required fields, see
|
||||
<a href="{@docRoot}guide/topics/search/adding-custom-suggestions.html#SuggestionTable">
|
||||
Building a suggestion table</a>.</p>
|
||||
|
||||
<p>The {@link android.app.SearchManager} class includes several columns for Android TV. Some of the
|
||||
more important columns are described below.</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Value</th>
|
||||
<th>Description</th>
|
||||
</tr><tr>
|
||||
<td>{@code SUGGEST_COLUMN_TEXT_1}</td>
|
||||
<td>The name of your content <strong>(required)</strong></td>
|
||||
</tr><tr>
|
||||
<td>{@code SUGGEST_COLUMN_TEXT_2}</td>
|
||||
<td>A text description of your content</td>
|
||||
</tr><tr>
|
||||
<td>{@code SUGGEST_COLUMN_RESULT_CARD_IMAGE}</td>
|
||||
<td>An image/poster/cover for your content</td>
|
||||
</tr><tr>
|
||||
<td>{@code SUGGEST_COLUMN_CONTENT_TYPE}</td>
|
||||
<td>The MIME type of your media <strong>(required)</strong></td>
|
||||
</tr><tr>
|
||||
<td>{@code SUGGEST_COLUMN_VIDEO_WIDTH}</td>
|
||||
<td>The resolution width of your media</td>
|
||||
</tr><tr>
|
||||
<td>{@code SUGGEST_COLUMN_VIDEO_HEIGHT}</td>
|
||||
<td>The resolution height of your media</td>
|
||||
</tr><tr>
|
||||
<td>{@code SUGGEST_COLUMN_PRODUCTION_YEAR}</td>
|
||||
<td>The production year of your content <strong>(required)</strong></td>
|
||||
</tr><tr>
|
||||
<td>{@code SUGGEST_COLUMN_DURATION}</td>
|
||||
<td>The duration in milliseconds of your media</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>The search framework requires the following columns:</p>
|
||||
<ul>
|
||||
<li>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}</li>
|
||||
<li>{@link android.app.SearchManager#SUGGEST_COLUMN_CONTENT_TYPE}</li>
|
||||
<li>{@link android.app.SearchManager#SUGGEST_COLUMN_PRODUCTION_YEAR}</li>
|
||||
</ul>
|
||||
|
||||
<p>When the values of these columns for your content match the values for the same content from other
|
||||
providers found by Google servers, the system provides a
|
||||
<a href="{@docRoot}training/app-indexing/deep-linking.html">deep link</a> to your app in the details
|
||||
view for the content, along with links to the apps of other providers. This is discussed more in
|
||||
<a href="#details">Display Content in the Details Screen</a>, below.</p>
|
||||
|
||||
<p>Your application's database class might define the columns as follows:</p>
|
||||
|
||||
<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/java/com/example/android/tvleanback/VideoDatabase.java#L41" target="_blank">
|
||||
VideoDatabase.java</a></p>
|
||||
<pre>
|
||||
public class VideoDatabase {
|
||||
//The columns we'll include in the video database table
|
||||
public static final String KEY_NAME = SearchManager.SUGGEST_COLUMN_TEXT_1;
|
||||
public static final String KEY_DESCRIPTION = SearchManager.SUGGEST_COLUMN_TEXT_2;
|
||||
public static final String KEY_ICON = SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE;
|
||||
public static final String KEY_DATA_TYPE = SearchManager.SUGGEST_COLUMN_CONTENT_TYPE;
|
||||
public static final String KEY_IS_LIVE = SearchManager.SUGGEST_COLUMN_IS_LIVE;
|
||||
public static final String KEY_VIDEO_WIDTH = SearchManager.SUGGEST_COLUMN_VIDEO_WIDTH;
|
||||
public static final String KEY_VIDEO_HEIGHT = SearchManager.SUGGEST_COLUMN_VIDEO_HEIGHT;
|
||||
public static final String KEY_AUDIO_CHANNEL_CONFIG =
|
||||
SearchManager.SUGGEST_COLUMN_AUDIO_CHANNEL_CONFIG;
|
||||
public static final String KEY_PURCHASE_PRICE = SearchManager.SUGGEST_COLUMN_PURCHASE_PRICE;
|
||||
public static final String KEY_RENTAL_PRICE = SearchManager.SUGGEST_COLUMN_RENTAL_PRICE;
|
||||
public static final String KEY_RATING_STYLE = SearchManager.SUGGEST_COLUMN_RATING_STYLE;
|
||||
public static final String KEY_RATING_SCORE = SearchManager.SUGGEST_COLUMN_RATING_SCORE;
|
||||
public static final String KEY_PRODUCTION_YEAR = SearchManager.SUGGEST_COLUMN_PRODUCTION_YEAR;
|
||||
public static final String KEY_COLUMN_DURATION = SearchManager.SUGGEST_COLUMN_DURATION;
|
||||
public static final String KEY_ACTION = SearchManager.SUGGEST_COLUMN_INTENT_ACTION;
|
||||
...
|
||||
</pre>
|
||||
|
||||
<p>When you build the map from the {@link android.app.SearchManager} columns to your data fields, you
|
||||
must also specify the {@link android.provider.BaseColumns#_ID} to give each row a unique ID.</p>
|
||||
|
||||
<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/java/com/example/android/tvleanback/VideoDatabase.java#L83" target="_blank">
|
||||
VideoDatabase.java</a></p>
|
||||
<pre>
|
||||
...
|
||||
private static HashMap<String, String> buildColumnMap() {
|
||||
HashMap<String, String> map = new HashMap<String, String>();
|
||||
map.put(KEY_NAME, KEY_NAME);
|
||||
map.put(KEY_DESCRIPTION, KEY_DESCRIPTION);
|
||||
map.put(KEY_ICON, KEY_ICON);
|
||||
map.put(KEY_DATA_TYPE, KEY_DATA_TYPE);
|
||||
map.put(KEY_IS_LIVE, KEY_IS_LIVE);
|
||||
map.put(KEY_VIDEO_WIDTH, KEY_VIDEO_WIDTH);
|
||||
map.put(KEY_VIDEO_HEIGHT, KEY_VIDEO_HEIGHT);
|
||||
map.put(KEY_AUDIO_CHANNEL_CONFIG, KEY_AUDIO_CHANNEL_CONFIG);
|
||||
map.put(KEY_PURCHASE_PRICE, KEY_PURCHASE_PRICE);
|
||||
map.put(KEY_RENTAL_PRICE, KEY_RENTAL_PRICE);
|
||||
map.put(KEY_RATING_STYLE, KEY_RATING_STYLE);
|
||||
map.put(KEY_RATING_SCORE, KEY_RATING_SCORE);
|
||||
map.put(KEY_PRODUCTION_YEAR, KEY_PRODUCTION_YEAR);
|
||||
map.put(KEY_COLUMN_DURATION, KEY_COLUMN_DURATION);
|
||||
map.put(KEY_ACTION, KEY_ACTION);
|
||||
map.put(BaseColumns._ID, "rowid AS " +
|
||||
BaseColumns._ID);
|
||||
map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " +
|
||||
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
|
||||
map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "rowid AS " +
|
||||
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
|
||||
return map;
|
||||
}
|
||||
...
|
||||
</pre>
|
||||
|
||||
<p>In the example above, notice the mapping to the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}
|
||||
field. This is the portion of the URI that points to the content unique to the data in this row —
|
||||
that is, the last part of the URI describing where the content is stored. The first part of the URI,
|
||||
when it is common to all of the rows in the table, is set in the
|
||||
<a href="{@docRoot}guide/topics/search/searchable-config.html"> {@code searchable.xml}</a> file as the
|
||||
<a href="{@docRoot}guide/topics/search/searchable-config.html#searchSuggestIntentData">
|
||||
{@code android:searchSuggestIntentData}</a> attribute, as described in
|
||||
<a href="#suggestions">Handle Search Suggestions</a>, below.
|
||||
|
||||
<p>If the first part of the URI is different for each row in the
|
||||
table, you map that value with the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} field.
|
||||
When the user selects this content, the intent that fires provides the intent data from the
|
||||
combination of the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}
|
||||
and either the {@code android:searchSuggestIntentData} attribute or the
|
||||
{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} field value.</p>
|
||||
|
||||
<h2 id="provide">Provide Search Suggestion Data</h2>
|
||||
|
||||
<p>Implement a <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Provider</a>
|
||||
to return search term suggestions to the Android TV search dialog. The system queries your content
|
||||
provider for suggestions by calling the {@link android.content.ContentProvider#query(android.net.Uri,
|
||||
java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) query()} method each time
|
||||
a letter is typed. In your implementation of {@link android.content.ContentProvider#query(android.net.Uri,
|
||||
java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) query()}, your content
|
||||
provider searches your suggestion data and returns a {@link android.database.Cursor} that points to
|
||||
the rows you have designated for suggestions.</p>
|
||||
|
||||
<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/java/com/example/android/tvleanback/VideoContentProvider.java" target="_blank">
|
||||
VideoContentProvider.java</a></p>
|
||||
<pre>
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
// Use the UriMatcher to see what kind of query we have and format the db query accordingly
|
||||
switch (URI_MATCHER.match(uri)) {
|
||||
case SEARCH_SUGGEST:
|
||||
Log.d(TAG, "search suggest: " + selectionArgs[0] + " URI: " + uri);
|
||||
if (selectionArgs == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"selectionArgs must be provided for the Uri: " + uri);
|
||||
}
|
||||
return getSuggestions(selectionArgs[0]);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown Uri: " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor getSuggestions(String query) {
|
||||
query = query.toLowerCase();
|
||||
String[] columns = new String[]{
|
||||
BaseColumns._ID,
|
||||
VideoDatabase.KEY_NAME,
|
||||
VideoDatabase.KEY_DESCRIPTION,
|
||||
VideoDatabase.KEY_ICON,
|
||||
VideoDatabase.KEY_DATA_TYPE,
|
||||
VideoDatabase.KEY_IS_LIVE,
|
||||
VideoDatabase.KEY_VIDEO_WIDTH,
|
||||
VideoDatabase.KEY_VIDEO_HEIGHT,
|
||||
VideoDatabase.KEY_AUDIO_CHANNEL_CONFIG,
|
||||
VideoDatabase.KEY_PURCHASE_PRICE,
|
||||
VideoDatabase.KEY_RENTAL_PRICE,
|
||||
VideoDatabase.KEY_RATING_STYLE,
|
||||
VideoDatabase.KEY_RATING_SCORE,
|
||||
VideoDatabase.KEY_PRODUCTION_YEAR,
|
||||
VideoDatabase.KEY_COLUMN_DURATION,
|
||||
VideoDatabase.KEY_ACTION,
|
||||
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
|
||||
};
|
||||
return mVideoDatabase.getWordMatch(query, columns);
|
||||
}
|
||||
...
|
||||
</pre>
|
||||
|
||||
<p>In your manifest file, the content provider receives special treatment. Rather than getting
|
||||
tagged as an activity, it is described as a
|
||||
<a href="{@docRoot}guide/topics/manifest/provider-element.html">{@code <provider>}</a>. The
|
||||
provider includes the {@code android:searchSuggestAuthority} attribute to tell the system the
|
||||
namespace of your content provider. Also, you must set its {@code android:exported} attribute to
|
||||
{@code "true"} so that the Android global search can use the results returned from it.</p>
|
||||
|
||||
<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/AndroidManifest.xml" target="_blank">
|
||||
AndroidManifest.xml</a></p>
|
||||
<pre>
|
||||
<provider android:name="com.example.android.tvleanback.VideoContentProvider"
|
||||
android:authorities="com.example.android.tvleanback"
|
||||
android:exported="true" />
|
||||
</pre>
|
||||
|
||||
<h2 id="suggestions">Handle Search Suggestions</h2>
|
||||
|
||||
<p>Your app must include a <a href="{@docRoot}guide/topics/search/searchable-config.html">
|
||||
{@code res/xml/searchable.xml}</a> file to configure the search suggestions settings. It inlcudes
|
||||
the <a href="{@docRoot}guide/topics/search/searchable-config.html#searchSuggestAuthority">
|
||||
{@code android:searchSuggestAuthority}</a> attribute to tell the system the namespace of your
|
||||
content provider. This must match the string value you specify in the
|
||||
<a href="{@docRoot}guide/topics/manifest/provider-element.html#auth">{@code android:authorities}</a>
|
||||
attribute of the <a href="{@docRoot}guide/topics/manifest/provider-element.html">{@code <provider>}
|
||||
</a> element in your {@code AndroidManifest.xml} file.</p>
|
||||
|
||||
The <a href="{@docRoot}guide/topics/search/searchable-config.html">{@code searchable.xml}</a> file
|
||||
must also include the <a href="{@docRoot}guide/topics/search/searchable-config.html#searchSuggestIntentAction">
|
||||
{@code android:searchSuggestIntentAction}</a> with the value {@code "android.intent.action.VIEW"}
|
||||
to define the intent action for providing a custom suggestion. This is different from the intent
|
||||
action for providing a search term, explained below. See also,
|
||||
<a href="{@docRoot}guide/topics/search/adding-custom-suggestions.html#IntentAction">Declaring the
|
||||
intent action</a> for other ways to declare the intent action for suggestions.</p>
|
||||
|
||||
<p>Along with the intent action, your app must provide the intent data, which you specify with the
|
||||
<a href="{@docRoot}guide/topics/search/searchable-config.html#searchSuggestIntentData">
|
||||
{@code android:searchSuggestIntentData}</a> attribute. This is the first part of the URI that points
|
||||
to the content. It describes the portion of the URI common to all rows in the mapping table for that
|
||||
content. The portion of the URI that is unique to each row is established with the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} field,
|
||||
as described above in <a href="#columns">Identify Columns</a>. See also,
|
||||
<a href="{@docRoot}guide/topics/search/adding-custom-suggestions.html#IntentData">
|
||||
Declaring the intent data</a> for other ways to declare the intent data for suggestions.</p>
|
||||
|
||||
<p>Also, note the {@code android:searchSuggestSelection=" ?"} attribute which specifies the value passed
|
||||
as the {@code selection} parameter of the {@link android.content.ContentProvider#query(android.net.Uri,
|
||||
java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) query()} method where the
|
||||
question mark ({@code ?}) value is replaced with the query text.</p>
|
||||
|
||||
<p>Finally, you must also include the <a href="{@docRoot}guide/topics/search/searchable-config.html#includeInGlobalSearch">
|
||||
{@code android:includeInGlobalSearch}</a> attribute with the value {@code "true"}. Here is an example
|
||||
<a href="{@docRoot}guide/topics/search/searchable-config.html">{@code searchable.xml}</a>
|
||||
file:</p>
|
||||
|
||||
<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/res/xml/searchable.xml" target="_blank">
|
||||
Searchable.xml</a></p>
|
||||
<pre>
|
||||
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:label="@string/search_label"
|
||||
android:hint="@string/search_hint"
|
||||
android:searchSettingsDescription="@string/settings_description"
|
||||
android:searchSuggestAuthority="com.example.android.tvleanback"
|
||||
android:searchSuggestIntentAction="android.intent.action.VIEW"
|
||||
android:searchSuggestIntentData="content://com.example.android.tvleanback/video_database_leanback"
|
||||
android:searchSuggestSelection=" ?"
|
||||
android:searchSuggestThreshold="1"
|
||||
android:includeInGlobalSearch="true"
|
||||
>
|
||||
</searchable>
|
||||
</pre>
|
||||
|
||||
<h2 id="terms">Handle Search Terms</h2>
|
||||
|
||||
<p>As soon as the search dialog has a word which matches the value in one of your app's columns
|
||||
(described in <a href="#identifying">Identifying Columns</a>, above), the system fires the
|
||||
{@link android.content.Intent#ACTION_SEARCH} intent. The activity in your app which handles that
|
||||
intent searches the repository for columns with the given word in their values, and returns a list
|
||||
of content items with those columns. In your {@code AndroidManifest.xml} file, you designate the
|
||||
activity which handles the {@link android.content.Intent#ACTION_SEARCH} intent like this:
|
||||
|
||||
<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/AndroidManifest.xml" target="_blank">
|
||||
AndroidManifest.xml</a></p>
|
||||
<pre>
|
||||
...
|
||||
<activity
|
||||
android:name="com.example.android.tvleanback.DetailsActivity"
|
||||
android:exported="true">
|
||||
|
||||
<!-- Receives the search request. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<!-- No category needed, because the Intent will specify this class component -->
|
||||
</intent-filter>
|
||||
|
||||
<!-- Points to searchable meta data. -->
|
||||
<meta-data android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
</activity>
|
||||
...
|
||||
<!-- Provides search suggestions for keywords against video meta data. -->
|
||||
<provider android:name="com.example.android.tvleanback.VideoContentProvider"
|
||||
android:authorities="com.example.android.tvleanback"
|
||||
android:exported="true" />
|
||||
...
|
||||
</pre>
|
||||
|
||||
<p>The activity must also describe the searchable configuration with a reference to the
|
||||
<a href="{@docRoot}guide/topics/search/searchable-config.html">{@code searchable.xml}</a> file.
|
||||
To <a href="{@docRoot}guide/topics/search/search-dialog.html">use the global search dialog</a>,
|
||||
the manifest must describe which activity should receive search queries. The manifest must also
|
||||
describe the <a href="{@docRoot}guide/topics/manifest/provider-element.html">{@code <provider>}
|
||||
</a>element, exactly as it is described in the <a href="{@docRoot}guide/topics/search/searchable-config.html">
|
||||
{@code searchable.xml}</a> file.</p>
|
||||
|
||||
<h2 id="details">Deep Link to Your App in the Details Screen</h2>
|
||||
|
||||
<p>If you have set up the search configuration as described in <a href="#suggestions">Handle Search
|
||||
Suggestions</a> and mapped the {@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1},
|
||||
{@link android.app.SearchManager#SUGGEST_COLUMN_CONTENT_TYPE}, and
|
||||
{@link android.app.SearchManager#SUGGEST_COLUMN_PRODUCTION_YEAR} fields as described in
|
||||
<a href="#columns">Identify Columns</a>, a <a href="{@docRoot}training/app-indexing/deep-linking.html">
|
||||
deep link</a> to your content appears in the details screen that launches when the user selects a
|
||||
search result.</p>
|
||||
|
||||
<p>When the user selects the link for your app, identified by the "Available On" button in the
|
||||
details screen, the system launches the activity which handles the {@link android.content.Intent#ACTION_VIEW}
|
||||
(set as <a href="{@docRoot}guide/topics/search/searchable-config.html#searchSuggestIntentAction">
|
||||
{@code android:searchSuggestIntentAction}</a> with the value {@code "android.intent.action.VIEW"} in
|
||||
the <a href="{@docRoot}guide/topics/search/searchable-config.html">{@code searchable.xml}</a> file).</p>
|
||||
|
||||
<p>You can also set up a custom intent to launch your activity, and this is demonstrated in the
|
||||
<a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android Leanback
|
||||
sample app</a>. Note that the sample app launches its own <code>LeanbackDetailsFragment</code> to
|
||||
show the details for the selected media, but you should launch the activity that plays the media
|
||||
immediately to save the user another click or two.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user