টিভি অ্যাপ্লিকেশানগুলি অনুসন্ধানযোগ্য করুন৷

অ্যান্ড্রয়েড টিভি ইনস্টল করা অ্যাপগুলো থেকে কন্টেন্টের ডেটা সংগ্রহ করতে এবং ব্যবহারকারীকে অনুসন্ধানের ফলাফল জানাতে অ্যান্ড্রয়েড সার্চ ইন্টারফেস ব্যবহার করে। এই ফলাফলগুলোর সাথে আপনার অ্যাপের কন্টেন্টের ডেটাও অন্তর্ভুক্ত করা যেতে পারে, যাতে ব্যবহারকারী আপনার অ্যাপের কন্টেন্টে তাৎক্ষণিক অ্যাক্সেস পান।

আপনার অ্যাপকে অবশ্যই অ্যান্ড্রয়েড টিভি-কে ডেটা ফিল্ড সরবরাহ করতে হবে, যেখান থেকে ব্যবহারকারী সার্চ ডায়ালগে অক্ষর টাইপ করার সাথে সাথে অ্যান্ড্রয়েড টিভি সাজেস্টেড সার্চ রেজাল্ট তৈরি করতে পারে। এটি করার জন্য, আপনার অ্যাপকে অবশ্যই একটি কন্টেন্ট প্রোভাইডার ইমপ্লিমেন্ট করতে হবে যা সাজেশনগুলো পরিবেশন করে। এর সাথে একটি searchable.xml কনফিগারেশন ফাইলও থাকতে হবে, যা কন্টেন্ট প্রোভাইডার এবং অ্যান্ড্রয়েড টিভির জন্য অন্যান্য গুরুত্বপূর্ণ তথ্য বর্ণনা করে। এছাড়াও, ব্যবহারকারী যখন কোনো সাজেস্টেড সার্চ রেজাল্ট নির্বাচন করে, তখন যে ইন্টেন্টটি ফায়ার হয়, সেটি পরিচালনা করার জন্য আপনার একটি অ্যাক্টিভিটি প্রয়োজন। আরও বিস্তারিত জানতে, "কাস্টম সার্চ সাজেশন যোগ করুন" দেখুন। এই নির্দেশিকাটি অ্যান্ড্রয়েড টিভি অ্যাপের জন্য নির্দিষ্ট প্রধান বিষয়গুলো আলোচনা করে।

এই নির্দেশিকাটি পড়ার আগে, সার্চ এপিআই (Search API) নির্দেশিকায় ব্যাখ্যা করা ধারণাগুলোর সাথে আপনি পরিচিত কিনা তা নিশ্চিত করুন। এছাড়াও, ‘সার্চ কার্যকারিতা যোগ করুন’ (Add search functionality) অংশটি পর্যালোচনা করুন।

এই নির্দেশিকার নমুনা কোডটি লিনব্যাক স্যাম্পল অ্যাপ থেকে নেওয়া হয়েছে।

কলামগুলি চিহ্নিত করুন

SearchManager তার প্রত্যাশিত ডেটা ফিল্ডগুলোকে একটি স্থানীয় ডাটাবেসের কলাম হিসেবে উপস্থাপন করে বর্ণনা করে। আপনার ডেটার ফরম্যাট যাই হোক না কেন, আপনাকে অবশ্যই আপনার ডেটা ফিল্ডগুলোকে এই কলামগুলোর সাথে ম্যাপ করতে হবে; সাধারণত সেই ক্লাসের মধ্যেই এটি করা হয় যা আপনার কন্টেন্ট ডেটা অ্যাক্সেস করে। আপনার বিদ্যমান ডেটাকে প্রয়োজনীয় ফিল্ডগুলোর সাথে ম্যাপ করে এমন একটি ক্লাস তৈরি করার বিষয়ে তথ্যের জন্য, “একটি সাজেশন টেবিল তৈরি করা” দেখুন।

SearchManager ক্লাসে অ্যান্ড্রয়েড টিভির জন্য বেশ কয়েকটি কলাম অন্তর্ভুক্ত রয়েছে। অধিক গুরুত্বপূর্ণ কয়েকটি কলাম নিম্নলিখিত সারণিতে বর্ণনা করা হলো।

মূল্য বর্ণনা
SUGGEST_COLUMN_TEXT_1 আপনার কন্টেন্টের নাম (আবশ্যক)
SUGGEST_COLUMN_TEXT_2 আপনার বিষয়বস্তুর একটি পাঠ্য বিবরণ
SUGGEST_COLUMN_RESULT_CARD_IMAGE আপনার কন্টেন্টের জন্য একটি ছবি, পোস্টার বা কভার।
SUGGEST_COLUMN_CONTENT_TYPE আপনার মিডিয়ার MIME টাইপ
SUGGEST_COLUMN_VIDEO_WIDTH আপনার মিডিয়ার রেজোলিউশন প্রস্থ
SUGGEST_COLUMN_VIDEO_HEIGHT আপনার মিডিয়ার রেজোলিউশন উচ্চতা
SUGGEST_COLUMN_PRODUCTION_YEAR আপনার কন্টেন্টের নির্মাণ বছর (আবশ্যক)
SUGGEST_COLUMN_DURATION আপনার মিডিয়ার সময়কাল (মিলিসেকেন্ডে) (আবশ্যক)

অনুসন্ধান কাঠামোটির জন্য নিম্নলিখিত কলামগুলো প্রয়োজন:

যখন আপনার কন্টেন্টের এই কলামগুলির মান, গুগল সার্ভার দ্বারা খুঁজে পাওয়া অন্যান্য প্রোভাইডারদের একই কন্টেন্টের মানের সাথে মিলে যায়, তখন সিস্টেমটি কন্টেন্টটির ডিটেইলস ভিউতে আপনার অ্যাপের একটি ডিপ লিঙ্ক প্রদান করে, সাথে অন্যান্য প্রোভাইডারদের অ্যাপের লিঙ্কও দিয়ে থাকে। এই বিষয়টি ‘ডিটেইলস স্ক্রিন’ বিভাগে আপনার অ্যাপের ডিপ লিঙ্ক সম্পর্কে আরও আলোচনা করা হয়েছে।

আপনার অ্যাপ্লিকেশনের ডাটাবেস ক্লাসটি কলামগুলোকে নিম্নরূপভাবে সংজ্ঞায়িত করতে পারে:

কোটলিন

class VideoDatabase {
    companion object {
        // The columns we'll include in the video database table
        val KEY_NAME = SearchManager.SUGGEST_COLUMN_TEXT_1
        val KEY_DESCRIPTION = SearchManager.SUGGEST_COLUMN_TEXT_2
        val KEY_ICON = SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE
        val KEY_DATA_TYPE = SearchManager.SUGGEST_COLUMN_CONTENT_TYPE
        val KEY_IS_LIVE = SearchManager.SUGGEST_COLUMN_IS_LIVE
        val KEY_VIDEO_WIDTH = SearchManager.SUGGEST_COLUMN_VIDEO_WIDTH
        val KEY_VIDEO_HEIGHT = SearchManager.SUGGEST_COLUMN_VIDEO_HEIGHT
        val KEY_AUDIO_CHANNEL_CONFIG = SearchManager.SUGGEST_COLUMN_AUDIO_CHANNEL_CONFIG
        val KEY_PURCHASE_PRICE = SearchManager.SUGGEST_COLUMN_PURCHASE_PRICE
        val KEY_RENTAL_PRICE = SearchManager.SUGGEST_COLUMN_RENTAL_PRICE
        val KEY_RATING_STYLE = SearchManager.SUGGEST_COLUMN_RATING_STYLE
        val KEY_RATING_SCORE = SearchManager.SUGGEST_COLUMN_RATING_SCORE
        val KEY_PRODUCTION_YEAR = SearchManager.SUGGEST_COLUMN_PRODUCTION_YEAR
        val KEY_COLUMN_DURATION = SearchManager.SUGGEST_COLUMN_DURATION
        val KEY_ACTION = SearchManager.SUGGEST_COLUMN_INTENT_ACTION
        ...
    }
    ...
}

জাভা

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;
...

যখন আপনি SearchManager কলামগুলো থেকে আপনার ডেটা ফিল্ডগুলোতে ম্যাপ তৈরি করবেন, তখন প্রতিটি সারিকে একটি অনন্য আইডি দেওয়ার জন্য আপনাকে অবশ্যই _ID টিও উল্লেখ করতে হবে।

কোটলিন

companion object {
    ....
    private fun buildColumnMap(): Map<String, String> {
        return mapOf(
          KEY_NAME to KEY_NAME,
          KEY_DESCRIPTION to KEY_DESCRIPTION,
          KEY_ICON to KEY_ICON,
          KEY_DATA_TYPE to KEY_DATA_TYPE,
          KEY_IS_LIVE to KEY_IS_LIVE,
          KEY_VIDEO_WIDTH to KEY_VIDEO_WIDTH,
          KEY_VIDEO_HEIGHT to KEY_VIDEO_HEIGHT,
          KEY_AUDIO_CHANNEL_CONFIG to KEY_AUDIO_CHANNEL_CONFIG,
          KEY_PURCHASE_PRICE to KEY_PURCHASE_PRICE,
          KEY_RENTAL_PRICE to KEY_RENTAL_PRICE,
          KEY_RATING_STYLE to KEY_RATING_STYLE,
          KEY_RATING_SCORE to KEY_RATING_SCORE,
          KEY_PRODUCTION_YEAR to KEY_PRODUCTION_YEAR,
          KEY_COLUMN_DURATION to KEY_COLUMN_DURATION,
          KEY_ACTION to KEY_ACTION,
          BaseColumns._ID to ("rowid AS " + BaseColumns._ID),
          SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID to ("rowid AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID),
          SearchManager.SUGGEST_COLUMN_SHORTCUT_ID to ("rowid AS " + SearchManager.SUGGEST_COLUMN_SHORTCUT_ID)
        )
    }
}

জাভা

...
  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;
  }
...

পূর্ববর্তী উদাহরণে, SUGGEST_COLUMN_INTENT_DATA_ID ফিল্ডের ম্যাপিংটি লক্ষ্য করুন। এটি URI-এর সেই অংশ যা এই সারির ডেটার জন্য অনন্য কন্টেন্টকে নির্দেশ করে—URI-এর শেষ অংশ, যা বর্ণনা করে কন্টেন্টটি কোথায় সংরক্ষিত আছে। URI-এর প্রথম অংশটি, যখন এটি টেবিলের সমস্ত সারির জন্য সাধারণ হয়, তখন 'সার্চ সাজেশন পরিচালনা' বিভাগে বর্ণিত পদ্ধতি অনুযায়ী searchable.xml ফাইলে android:searchSuggestIntentData অ্যাট্রিবিউট হিসাবে সেট করা হয়।

যদি টেবিলের প্রতিটি সারির জন্য URI-এর প্রথম অংশ ভিন্ন হয়, তাহলে সেই মানটিকে SUGGEST_COLUMN_INTENT_DATA ফিল্ডের সাথে ম্যাপ করুন। যখন ব্যবহারকারী এই কন্টেন্টটি নির্বাচন করেন, তখন যে ইন্টেন্টটি ফায়ার হয়, সেটি SUGGEST_COLUMN_INTENT_DATA_ID এবং android:searchSuggestIntentData অ্যাট্রিবিউট অথবা SUGGEST_COLUMN_INTENT_DATA ফিল্ডের মানের সমন্বয় থেকে ইন্টেন্ট ডেটা সরবরাহ করে।

অনুসন্ধানের পরামর্শ ডেটা সরবরাহ করুন

অ্যান্ড্রয়েড টিভি সার্চ ডায়ালগে সার্চ টার্ম সাজেশন ফেরত দেওয়ার জন্য একটি কন্টেন্ট প্রোভাইডার ইমপ্লিমেন্ট করুন। প্রতিবার একটি অক্ষর টাইপ করার সময় সিস্টেম query() ` মেথড কল করার মাধ্যমে সাজেশনের জন্য আপনার কন্টেন্ট প্রোভাইডারকে কোয়েরি করে। query() -এর ইমপ্লিমেন্টেশনে, আপনার কন্টেন্ট প্রোভাইডার সাজেশনের ডেটা সার্চ করে এবং একটি Cursor ফেরত দেয়, যা সাজেশনের জন্য আপনার নির্ধারিত সারিগুলোকে নির্দেশ করে।

কোটলিন

fun query(uri: Uri, projection: Array<String>, selection: String, selectionArgs: Array<String>,
        sortOrder: String): Cursor {
    // Use the UriMatcher to see what kind of query we have and format the db query accordingly
    when (URI_MATCHER.match(uri)) {
        SEARCH_SUGGEST -> {
            Log.d(TAG, "search suggest: ${selectionArgs[0]} URI: $uri")
            if (selectionArgs == null) {
                throw IllegalArgumentException(
                        "selectionArgs must be provided for the Uri: $uri")
            }
            return getSuggestions(selectionArgs[0])
        }
        else -> throw IllegalArgumentException("Unknown Uri: $uri")
    }
}

private fun getSuggestions(query: String): Cursor {
    val columns = arrayOf<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 videoDatabase.getWordMatch(query.toLowerCase(), columns)
}

জাভা

@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 videoDatabase.getWordMatch(query, columns);
}
...

আপনার ম্যানিফেস্ট ফাইলে কন্টেন্ট প্রোভাইডার একটি বিশেষ স্থান পায়। এটিকে অ্যাক্টিভিটি হিসেবে ট্যাগ করার পরিবর্তে, <provider> হিসেবে বর্ণনা করা হয়। প্রোভাইডারটিতে android:authorities অ্যাট্রিবিউটটি অন্তর্ভুক্ত থাকে, যা সিস্টেমকে আপনার কন্টেন্ট প্রোভাইডারের নেমস্পেস সম্পর্কে জানায়। এছাড়াও, আপনাকে এর android:exported অ্যাট্রিবিউটটি "true" সেট করতে হবে, যাতে অ্যান্ড্রয়েড গ্লোবাল সার্চ এর থেকে প্রাপ্ত ফলাফলগুলো ব্যবহার করতে পারে।

<provider android:name="com.example.android.tvleanback.VideoContentProvider"
    android:authorities="com.example.android.tvleanback"
    android:exported="true" />

অনুসন্ধানের পরামর্শগুলি পরিচালনা করুন

সার্চ সাজেশনের সেটিংস কনফিগার করার জন্য আপনার অ্যাপে অবশ্যই একটি res/xml/searchable.xml ফাইল অন্তর্ভুক্ত থাকতে হবে।

res/xml/searchable.xml ফাইলে, আপনার কন্টেন্ট প্রোভাইডারের নেমস্পেস সিস্টেমকে জানানোর জন্য android:searchSuggestAuthority অ্যাট্রিবিউটটি অন্তর্ভুক্ত করুন। এটি অবশ্যই আপনার AndroidManifest.xml ফাইলের <provider> এলিমেন্টের android:authorities অ্যাট্রিবিউটে নির্দিষ্ট করা স্ট্রিং মানের সাথে মিলতে হবে।

এছাড়াও একটি লেবেল অন্তর্ভুক্ত করুন, যা অ্যাপ্লিকেশনটির নাম। সিস্টেম সার্চ সেটিংস অনুসন্ধানযোগ্য অ্যাপগুলো তালিকাভুক্ত করার সময় এই লেবেলটি ব্যবহার করে।

কাস্টম সাজেশন দেওয়ার জন্য ইন্টেন্ট অ্যাকশন নির্ধারণ করতে, searchable.xml ফাইলে অবশ্যই android:searchSuggestIntentAction টি অন্তর্ভুক্ত করতে হবে এবং এর ভ্যালু হিসেবে "android.intent.action.VIEW" এটি সার্চ টার্ম দেওয়ার ইন্টেন্ট অ্যাকশন থেকে ভিন্ন, যা পরবর্তী সেকশনে বর্ণনা করা হয়েছে। সাজেশনের জন্য ইন্টেন্ট অ্যাকশন ডিক্লেয়ার করার অন্যান্য পদ্ধতির জন্য, "ডিক্লেয়ারিং দ্য ইন্টেন্ট অ্যাকশন" দেখুন।

ইনটেন্ট অ্যাকশনের পাশাপাশি, আপনার অ্যাপকে অবশ্যই ইনটেন্ট ডেটা প্রদান করতে হবে, যা আপনি android:searchSuggestIntentData অ্যাট্রিবিউটের মাধ্যমে নির্দিষ্ট করেন। এটি হলো কন্টেন্টকে নির্দেশকারী URI-এর প্রথম অংশ, যা সেই কন্টেন্টের জন্য ম্যাপিং টেবিলের সমস্ত সারির সাধারণ URI-এর অংশটিকে বর্ণনা করে। URI-এর যে অংশটি প্রতিটি সারির জন্য স্বতন্ত্র, তা ‘কলাম শনাক্তকরণ’ ( Identify columns ) বিভাগে বর্ণিত SUGGEST_COLUMN_INTENT_DATA_ID ফিল্ডের মাধ্যমে প্রতিষ্ঠিত হয়। সাজেশনের জন্য ইনটেন্ট ডেটা ঘোষণা করার অন্যান্য পদ্ধতির জন্য, ‘ইন্টেন্ট ডেটা ঘোষণা’ (Declaring the intent data) দেখুন।

` android:searchSuggestSelection=" ?" ` অ্যাট্রিবিউটটি query() ` মেথডের selection প্যারামিটার হিসেবে পাস করা মান নির্দিষ্ট করে। প্রশ্নবোধক চিহ্ন ( ? ) এর মানটি কোয়েরি টেক্সট দ্বারা প্রতিস্থাপিত হয়।

সবশেষে, আপনাকে অবশ্যই android:includeInGlobalSearch অ্যাট্রিবিউটটি "true" ভ্যালু সহ অন্তর্ভুক্ত করতে হবে। নিচে একটি searchable.xml ফাইলের উদাহরণ দেওয়া হলো:

<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>

অনুসন্ধানের শর্তাবলী পরিচালনা করুন

‘কলাম শনাক্তকরণ’ বিভাগে বর্ণিত পদ্ধতি অনুযায়ী, সার্চ ডায়ালগে আপনার অ্যাপের কোনো একটি কলামের মানের সাথে মিলে যায় এমন কোনো শব্দ পাওয়া মাত্রই, সিস্টেম ACTION_SEARCH ইন্টেন্টটি ফায়ার করে। আপনার অ্যাপের যে অ্যাক্টিভিটি এই ইন্টেন্টটি পরিচালনা করে, সেটি রিপোজিটরিতে সেইসব কলাম অনুসন্ধান করে যেগুলোর মানে প্রদত্ত শব্দটি রয়েছে এবং সেই কলামগুলোসহ কন্টেন্ট আইটেমগুলোর একটি তালিকা ফেরত দেয়। আপনার AndroidManifest.xml ফাইলে, আপনি ACTION_SEARCH ইন্টেন্টটি পরিচালনাকারী অ্যাক্টিভিটিকে নির্দিষ্ট করে দেন, যা নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

...
  <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" />
...

অ্যাক্টিভিটিতে অবশ্যই searchable.xml ফাইলের রেফারেন্স সহ সার্চযোগ্য কনফিগারেশন বর্ণনা করতে হবে। গ্লোবাল সার্চ ডায়ালগ ব্যবহার করার জন্য, ম্যানিফেস্টে অবশ্যই বর্ণনা করতে হবে কোন অ্যাক্টিভিটি সার্চ কোয়েরি গ্রহণ করবে। ম্যানিফেস্টে অবশ্যই <provider> এলিমেন্টটিও বর্ণনা করতে হবে, ঠিক যেমনটি searchable.xml ফাইলে বর্ণনা করা আছে।

ডিটেইলস স্ক্রিনে আপনার অ্যাপের ডিপ লিঙ্ক

যদি আপনি 'সার্চ সাজেশন পরিচালনা' বিভাগে বর্ণিত পদ্ধতি অনুযায়ী সার্চ কনফিগারেশন সেট আপ করে থাকেন এবং 'কলাম শনাক্তকরণ' বিভাগে বর্ণিত পদ্ধতি অনুযায়ী SUGGEST_COLUMN_TEXT_1 , SUGGEST_COLUMN_PRODUCTION_YEAR , ও SUGGEST_COLUMN_DURATION ফিল্ডগুলো ম্যাপ করে থাকেন, তাহলে ব্যবহারকারী কোনো সার্চ রেজাল্ট নির্বাচন করার পর যে ডিটেইলস স্ক্রিনটি চালু হয়, সেখানে আপনার কন্টেন্টের জন্য একটি ওয়াচ অ্যাকশনের ডিপ লিঙ্ক দেখা যায়:

বিস্তারিত স্ক্রিনে ডিপ লিঙ্ক
চিত্র ১. ডিটেইলস স্ক্রিনের ডিপ লিঙ্ক।

যখন ব্যবহারকারী ডিটেইলস স্ক্রিনে থাকা **Available On** বাটন দ্বারা চিহ্নিত আপনার অ্যাপের লিঙ্কটি নির্বাচন করেন, তখন সিস্টেম সেই অ্যাক্টিভিটিটি চালু করে যা searchable.xml ফাইলে android:searchSuggestIntentAction হিসেবে সেট করা এবং "android.intent.action.VIEW" ভ্যালুযুক্ত ACTION_VIEW হ্যান্ডেল করে।

আপনি আপনার অ্যাক্টিভিটি চালু করার জন্য একটি কাস্টম ইন্টেন্টও সেট আপ করতে পারেন। এটি লিনব্যাক স্যাম্পল অ্যাপে দেখানো হয়েছে। লক্ষ্য করুন যে, স্যাম্পল অ্যাপটি নির্বাচিত মিডিয়ার বিবরণ দেখানোর জন্য তার নিজস্ব LeanbackDetailsFragment চালু করে; আপনার অ্যাপে, ব্যবহারকারীর আরও এক বা দুটি ক্লিক বাঁচানোর জন্য মিডিয়াটি প্লে করার অ্যাক্টিভিটিটি অবিলম্বে চালু করুন।

অনুসন্ধানের আচরণ

অ্যান্ড্রয়েড টিভিতে হোম স্ক্রিন এবং আপনার অ্যাপের ভেতর থেকে সার্চ করা যায়। এই দুই ক্ষেত্রে সার্চের ফলাফল ভিন্ন হয়।

হোম স্ক্রিন থেকে অনুসন্ধান করুন

যখন ব্যবহারকারী হোম স্ক্রীন থেকে অনুসন্ধান করেন, তখন প্রথম ফলাফলটি একটি এনটিটি কার্ডে প্রদর্শিত হয়। যদি সেই কন্টেন্টটি চালাতে পারে এমন কোনো অ্যাপ থাকে, তাহলে কার্ডটির নীচে সেগুলোর প্রত্যেকটির একটি করে লিঙ্ক দেখা যায়:

টিভি অনুসন্ধানের ফলাফল প্লেব্যাক
চিত্র ২. হোম স্ক্রিনের অনুসন্ধানের ফলাফল।

আপনি প্রোগ্রাম্যাটিকভাবে এনটিটি কার্ডে কোনো অ্যাপ রাখতে পারবেন না। প্লেব্যাক অপশন হিসেবে অন্তর্ভুক্ত হতে হলে, একটি অ্যাপের সার্চ রেজাল্টকে অবশ্যই সার্চ করা কন্টেন্টের টাইটেল, বছর এবং সময়কালের সাথে মিলতে হবে।

কার্ডের নিচে আরও অনুসন্ধানের ফলাফল থাকতে পারে। সেগুলো দেখতে হলে, ব্যবহারকারীকে রিমোট চেপে ধরে স্ক্রল করতে হবে। প্রতিটি অ্যাপের ফলাফল আলাদা সারিতে প্রদর্শিত হয়। আপনি সারির ক্রম নিয়ন্ত্রণ করতে পারবেন না। যে অ্যাপগুলো ওয়াচ অ্যাকশন সমর্থন করে, সেগুলো প্রথমে তালিকাভুক্ত করা হয়।

অতিরিক্ত টিভি অনুসন্ধানের ফলাফল
চিত্র ৩. অতিরিক্ত অনুসন্ধানের ফলাফল দেখা।

আপনার অ্যাপ থেকে অনুসন্ধান করুন

ব্যবহারকারী রিমোট বা গেমপ্যাড কন্ট্রোলার থেকে মাইক্রোফোন চালু করে আপনার অ্যাপের ভেতর থেকেও অনুসন্ধান শুরু করতে পারেন। অনুসন্ধানের ফলাফলগুলো অ্যাপের বিষয়বস্তুর উপরে একটি সারিতে প্রদর্শিত হয়। আপনার অ্যাপ তার নিজস্ব গ্লোবাল সার্চ প্রোভাইডার ব্যবহার করে অনুসন্ধানের ফলাফল তৈরি করে।

টিভি ইন-অ্যাপ অনুসন্ধানের ফলাফল
চিত্র ৪. অ্যাপের অভ্যন্তরীণ অনুসন্ধানের ফলাফল।

আরও জানুন

টিভি অ্যাপে অনুসন্ধান সম্পর্কে আরও জানতে, ‘আপনার অ্যাপে অ্যান্ড্রয়েড অনুসন্ধান বৈশিষ্ট্য একীভূত করুন’ এবং ‘অনুসন্ধান কার্যকারিতা যোগ করুন’ পড়ুন।

SearchFragment ব্যবহার করে অ্যাপের ভেতরের সার্চ অভিজ্ঞতা কীভাবে কাস্টমাইজ করা যায় সে সম্পর্কে আরও তথ্যের জন্য, “টিভি অ্যাপের মধ্যে সার্চ” পড়ুন।