অ্যান্ড্রয়েড এন এবং তার আগের সুপারিশগুলি৷

টিভি ব্যবহারের ক্ষেত্রে, ব্যবহারকারীরা সাধারণত কোনো কিছু দেখার আগে ন্যূনতম হস্তক্ষেপ করতে পছন্দ করেন। অনেক টিভি ব্যবহারকারীর জন্য আদর্শ পরিস্থিতি হলো: বসে পড়া, টিভি চালু করা এবং দেখা। ব্যবহারকারীরা যে কন্টেন্ট উপভোগ করেন, সেখানে পৌঁছানোর জন্য যে পথটিতে সবচেয়ে কম ধাপ থাকে, সাধারণত তারাই সেই পথটি বেছে নেন।

দ্রষ্টব্য: এখানে বর্ণিত API-গুলি শুধুমাত্র Android 7.1 (API লেভেল 25) পর্যন্ত সংস্করণে চালিত অ্যাপে সুপারিশ করার জন্য ব্যবহার করুন। Android 8.0 (API লেভেল 26) এবং তার পরবর্তী সংস্করণে চালিত অ্যাপে সুপারিশ সরবরাহ করার জন্য, আপনার অ্যাপকে অবশ্যই সুপারিশ চ্যানেল ব্যবহার করতে হবে।

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

অ্যান্ড্রয়েড টিভির হোম স্ক্রিনে সুপারিশ সারির একটি উদাহরণ
চিত্র ১. সুপারিশ সারির একটি উদাহরণ।

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

সুপারিশের জন্য সর্বোত্তম অনুশীলন

সুপারিশগুলো ব্যবহারকারীদের তাদের পছন্দের কন্টেন্ট এবং অ্যাপগুলো দ্রুত খুঁজে পেতে সাহায্য করে। আপনার টিভি অ্যাপে একটি চমৎকার ব্যবহারকারীর অভিজ্ঞতা তৈরি করার জন্য, ব্যবহারকারীদের জন্য উন্নত মানের এবং প্রাসঙ্গিক সুপারিশ তৈরি করা একটি গুরুত্বপূর্ণ বিষয়। এই কারণে, আপনি ব্যবহারকারীকে কী ধরনের সুপারিশ দেখাবেন তা সতর্কতার সাথে বিবেচনা করা এবং সেগুলোকে নিবিড়ভাবে পরিচালনা করা উচিত।

সুপারিশের প্রকারভেদ

যখন আপনি সুপারিশ তৈরি করবেন, তখন ব্যবহারকারীদের অসমাপ্ত দেখার কার্যকলাপের সাথে লিঙ্ক করে দিন অথবা এমন কার্যকলাপের পরামর্শ দিন যা সেটিকে সম্পর্কিত বিষয়বস্তুতে প্রসারিত করে। এখানে কিছু নির্দিষ্ট ধরণের সুপারিশ দেওয়া হলো যা আপনি বিবেচনা করতে পারেন:

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

সর্বোত্তম ব্যবহারকারীর অভিজ্ঞতার জন্য কীভাবে সুপারিশ কার্ড ডিজাইন করতে হয় সে সম্পর্কে আরও তথ্যের জন্য, Android TV ডিজাইন স্পেক-এর Recommendation Row দেখুন।

সুপারিশগুলি রিফ্রেশ করুন

সুপারিশগুলো রিফ্রেশ করার সময়, শুধু সেগুলো মুছে ফেলে আবার পোস্ট করবেন না, কারণ এমনটা করলে সুপারিশগুলো তালিকার একেবারে শেষে চলে আসে। কোনো কন্টেন্ট, যেমন একটি সিনেমা, একবার প্লে করা হয়ে গেলে, সেটিকে সুপারিশ থেকে সরিয়ে দিন

সুপারিশগুলি কাস্টমাইজ করুন

আপনি কার্ডের ফোরগ্রাউন্ড ও ব্যাকগ্রাউন্ড ইমেজ, রঙ, অ্যাপ আইকন, টাইটেল এবং সাবটাইটেলের মতো ইউজার ইন্টারফেস এলিমেন্ট সেট করার মাধ্যমে ব্র্যান্ডিং তথ্য তুলে ধরতে রেকমেন্ডেশন কার্ডগুলো কাস্টমাইজ করতে পারেন। আরও জানতে, অ্যান্ড্রয়েড টিভি ডিজাইন স্পেক-এর রেকমেন্ডেশন রো দেখুন।

দলগত সুপারিশ

আপনি চাইলে সুপারিশের উৎসের উপর ভিত্তি করে সুপারিশগুলোকে গ্রুপ করতে পারেন। উদাহরণস্বরূপ, আপনার অ্যাপ দুই ধরনের সুপারিশ প্রদান করতে পারে: ব্যবহারকারী যে কন্টেন্টে সাবস্ক্রাইব করেছেন তার জন্য সুপারিশ, এবং নতুন ট্রেন্ডিং কন্টেন্টের জন্য সুপারিশ, যা সম্পর্কে ব্যবহারকারী হয়তো অবগত নন।

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

কোনো সুপারিশের গ্রুপ কী স্ট্রিং সেট করতে NotificationCompat.Builder.setGroup() ব্যবহার করুন। উদাহরণস্বরূপ, কোনো সুপারিশকে নতুন ট্রেন্ডিং কন্টেন্ট ধারণকারী একটি গ্রুপের অন্তর্ভুক্ত হিসেবে চিহ্নিত করতে, আপনি setGroup("trending") কল করতে পারেন।

একটি সুপারিশ পরিষেবা তৈরি করুন

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

নিম্নলিখিত কোড উদাহরণটি দেখায় কিভাবে আপনার অ্যাপ্লিকেশনের জন্য একটি সুপারিশ পরিষেবা তৈরি করতে IntentService প্রসারিত করা যায়:

কোটলিন

class UpdateRecommendationsService : IntentService("RecommendationService") {
    override protected fun onHandleIntent(intent: Intent) {
        Log.d(TAG, "Updating recommendation cards")
        val recommendations = VideoProvider.getMovieList()
        if (recommendations == null) return

        var count = 0

        try {
            val builder = RecommendationBuilder()
                    .setContext(applicationContext)
                    .setSmallIcon(R.drawable.videos_by_google_icon)

            for (entry in recommendations.entrySet()) {
                for (movie in entry.getValue()) {
                    Log.d(TAG, "Recommendation - " + movie.getTitle())

                    builder.setBackground(movie.getCardImageUrl())
                            .setId(count + 1)
                            .setPriority(MAX_RECOMMENDATIONS - count)
                            .setTitle(movie.getTitle())
                            .setDescription(getString(R.string.popular_header))
                            .setImage(movie.getCardImageUrl())
                            .setIntent(buildPendingIntent(movie))
                            .build()
                    if (++count >= MAX_RECOMMENDATIONS) {
                        break
                    }
                }
                if (++count >= MAX_RECOMMENDATIONS) {
                    break
                }
            }
        } catch (e: IOException) {
            Log.e(TAG, "Unable to update recommendation", e)
        }
    }

    private fun buildPendingIntent(movie: Movie): PendingIntent {
        val detailsIntent = Intent(this, DetailsActivity::class.java)
        detailsIntent.putExtra("Movie", movie)

        val stackBuilder = TaskStackBuilder.create(this)
        stackBuilder.addParentStack(DetailsActivity::class.java)
        stackBuilder.addNextIntent(detailsIntent)

        // Ensure a unique PendingIntents, otherwise all
        // recommendations end up with the same PendingIntent
        detailsIntent.setAction(movie.getId().toString())

        val intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
        return intent
    }

    companion object {
        private val TAG = "UpdateRecommendationsService"
        private val MAX_RECOMMENDATIONS = 3
    }
}

জাভা

public class UpdateRecommendationsService extends IntentService {
    private static final String TAG = "UpdateRecommendationsService";
    private static final int MAX_RECOMMENDATIONS = 3;

    public UpdateRecommendationsService() {
        super("RecommendationService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "Updating recommendation cards");
        HashMap<String, List<Movie>> recommendations = VideoProvider.getMovieList();
        if (recommendations == null) return;

        int count = 0;

        try {
            RecommendationBuilder builder = new RecommendationBuilder()
                    .setContext(getApplicationContext())
                    .setSmallIcon(R.drawable.videos_by_google_icon);

            for (Map.Entry<String, List<Movie>> entry : recommendations.entrySet()) {
                for (Movie movie : entry.getValue()) {
                    Log.d(TAG, "Recommendation - " + movie.getTitle());

                    builder.setBackground(movie.getCardImageUrl())
                            .setId(count + 1)
                            .setPriority(MAX_RECOMMENDATIONS - count)
                            .setTitle(movie.getTitle())
                            .setDescription(getString(R.string.popular_header))
                            .setImage(movie.getCardImageUrl())
                            .setIntent(buildPendingIntent(movie))
                            .build();

                    if (++count >= MAX_RECOMMENDATIONS) {
                        break;
                    }
                }
                if (++count >= MAX_RECOMMENDATIONS) {
                    break;
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "Unable to update recommendation", e);
        }
    }

    private PendingIntent buildPendingIntent(Movie movie) {
        Intent detailsIntent = new Intent(this, DetailsActivity.class);
        detailsIntent.putExtra("Movie", movie);

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(DetailsActivity.class);
        stackBuilder.addNextIntent(detailsIntent);
        // Ensure a unique PendingIntents, otherwise all
        // recommendations end up with the same PendingIntent
        detailsIntent.setAction(Long.toString(movie.getId()));

        PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
        return intent;
    }
}

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

<manifest ... >
  <application ... >
    ...

    <service
            android:name="com.example.android.tvleanback.UpdateRecommendationsService"
            android:enabled="true" />
  </application>
</manifest>

সুপারিশ তৈরি করুন

আপনার সুপারিশ পরিষেবাটি চালু হয়ে গেলে, এটিকে অবশ্যই সুপারিশ তৈরি করে অ্যান্ড্রয়েড ফ্রেমওয়ার্কের কাছে পাঠাতে হবে। ফ্রেমওয়ার্কটি এই সুপারিশগুলোকে Notification অবজেক্ট হিসেবে গ্রহণ করে, যেগুলো একটি নির্দিষ্ট টেমপ্লেট ব্যবহার করে এবং একটি নির্দিষ্ট ক্যাটাগরি দ্বারা চিহ্নিত থাকে।

মান নির্ধারণ করা

রেকমেন্ডেশন কার্ডের UI এলিমেন্টের মান সেট করার জন্য, আপনাকে নিচে বর্ণিত বিল্ডার প্যাটার্ন অনুসরণ করে একটি বিল্ডার ক্লাস তৈরি করতে হবে। প্রথমে, আপনাকে রেকমেন্ডেশন কার্ডের এলিমেন্টগুলোর মান সেট করতে হবে।

কোটলিন

class RecommendationBuilder {
    ...

    fun setTitle(title: String): RecommendationBuilder {
        this.title = title
        return this
    }

    fun setDescription(description: String): RecommendationBuilder {
        this.description = description
        return this
    }

    fun setImage(uri: String): RecommendationBuilder {
        imageUri = uri
        return this
    }

    fun setBackground(uri: String): RecommendationBuilder {
        backgroundUri = uri
        return this
    }

...

জাভা

public class RecommendationBuilder {
    ...

    public RecommendationBuilder setTitle(String title) {
            this.title = title;
            return this;
        }

        public RecommendationBuilder setDescription(String description) {
            this.description = description;
            return this;
        }

        public RecommendationBuilder setImage(String uri) {
            imageUri = uri;
            return this;
        }

        public RecommendationBuilder setBackground(String uri) {
            backgroundUri = uri;
            return this;
        }
...

বিজ্ঞপ্তি তৈরি করুন

একবার মানগুলো সেট করে ফেলার পর, আপনি নোটিফিকেশনটি বিল্ড করবেন, বিল্ডার ক্লাস থেকে মানগুলো নোটিফিকেশনে অ্যাসাইন করবেন এবং NotificationCompat.Builder.build() কল করবেন।

এছাড়াও, setLocalOnly() কল করতে ভুলবেন না, যাতে NotificationCompat.BigPictureStyle নোটিফিকেশনটি অন্য ডিভাইসে দেখা না যায়।

নিম্নলিখিত কোড উদাহরণটি দেখায় কিভাবে একটি সুপারিশ তৈরি করতে হয়।

কোটলিন

class RecommendationBuilder {
    ...

    @Throws(IOException::class)
    fun build(): Notification {
        ...

        val notification = NotificationCompat.BigPictureStyle(
        NotificationCompat.Builder(context)
                .setContentTitle(title)
                .setContentText(description)
                .setPriority(priority)
                .setLocalOnly(true)
                .setOngoing(true)
                .setColor(context.resources.getColor(R.color.fastlane_background))
                .setCategory(Notification.CATEGORY_RECOMMENDATION)
                .setLargeIcon(image)
                .setSmallIcon(smallIcon)
                .setContentIntent(intent)
                .setExtras(extras))
                .build()

        return notification
    }
}

জাভা

public class RecommendationBuilder {
    ...

    public Notification build() throws IOException {
        ...

        Notification notification = new NotificationCompat.BigPictureStyle(
                new NotificationCompat.Builder(context)
                        .setContentTitle(title)
                        .setContentText(description)
                        .setPriority(priority)
                        .setLocalOnly(true)
                        .setOngoing(true)
                        .setColor(context.getResources().getColor(R.color.fastlane_background))
                        .setCategory(Notification.CATEGORY_RECOMMENDATION)
                        .setLargeIcon(image)
                        .setSmallIcon(smallIcon)
                        .setContentIntent(intent)
                        .setExtras(extras))
                .build();

        return notification;
    }
}

সুপারিশ পরিষেবা চালান

বর্তমান সুপারিশগুলো তৈরি করার জন্য আপনার অ্যাপের সুপারিশ পরিষেবাটি অবশ্যই নির্দিষ্ট সময় পর পর চলতে হবে। আপনার পরিষেবাটি চালানোর জন্য, এমন একটি ক্লাস তৈরি করুন যা একটি টাইমার চালায় এবং নিয়মিত বিরতিতে সেটিকে কল করে। নিম্নলিখিত কোড উদাহরণটি প্রতি আধা ঘণ্টা পর পর একটি সুপারিশ পরিষেবা চালু করার জন্য BroadcastReceiver ক্লাসটিকে এক্সটেন্ড করে:

কোটলিন

class BootupActivity : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d(TAG, "BootupActivity initiated")
        if (intent.action.endsWith(Intent.ACTION_BOOT_COMPLETED)) {
            scheduleRecommendationUpdate(context)
        }
    }

    private fun scheduleRecommendationUpdate(context: Context) {
        Log.d(TAG, "Scheduling recommendations update")
        val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val recommendationIntent = Intent(context, UpdateRecommendationsService::class.java)
        val alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0)
        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                INITIAL_DELAY,
                AlarmManager.INTERVAL_HALF_HOUR,
                alarmIntent
        )
    }

    companion object {
        private val TAG = "BootupActivity"
        private val INITIAL_DELAY:Long = 5000
    }
}

জাভা

public class BootupActivity extends BroadcastReceiver {
    private static final String TAG = "BootupActivity";

    private static final long INITIAL_DELAY = 5000;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "BootupActivity initiated");
        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
            scheduleRecommendationUpdate(context);
        }
    }

    private void scheduleRecommendationUpdate(Context context) {
        Log.d(TAG, "Scheduling recommendations update");

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class);
        PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0);

        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                INITIAL_DELAY,
                AlarmManager.INTERVAL_HALF_HOUR,
                alarmIntent);
    }
}

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

<manifest ... >
  <application ... >
    <receiver android:name="com.example.android.tvleanback.BootupActivity"
              android:enabled="true"
              android:exported="false">
      <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
      </intent-filter>
    </receiver>
  </application>
</manifest>

গুরুত্বপূর্ণ: বুট সম্পন্ন হওয়ার নোটিফিকেশন পেতে হলে আপনার অ্যাপকে RECEIVE_BOOT_COMPLETED পারমিশনটির জন্য অনুরোধ করতে হবে। আরও তথ্যের জন্য, ACTION_BOOT_COMPLETED দেখুন।

আপনার রিকমেন্ডেশন সার্ভিস ক্লাসের onHandleIntent() মেথডে, ম্যানেজারের কাছে রিকমেন্ডেশনটি নিম্নরূপে পোস্ট করুন:

কোটলিন

val notification = notificationBuilder.build()
notificationManager.notify(id, notification)

জাভা

Notification notification = notificationBuilder.build();
notificationManager.notify(id, notification);
,

টিভি ব্যবহারের ক্ষেত্রে, ব্যবহারকারীরা সাধারণত কোনো কিছু দেখার আগে ন্যূনতম হস্তক্ষেপ করতে পছন্দ করেন। অনেক টিভি ব্যবহারকারীর জন্য আদর্শ পরিস্থিতি হলো: বসে পড়া, টিভি চালু করা এবং দেখা। ব্যবহারকারীরা যে কন্টেন্ট উপভোগ করেন, সেখানে পৌঁছানোর জন্য যে পথটিতে সবচেয়ে কম ধাপ থাকে, সাধারণত তারাই সেই পথটি বেছে নেন।

দ্রষ্টব্য: এখানে বর্ণিত API-গুলি শুধুমাত্র Android 7.1 (API লেভেল 25) পর্যন্ত সংস্করণে চালিত অ্যাপে সুপারিশ করার জন্য ব্যবহার করুন। Android 8.0 (API লেভেল 26) এবং তার পরবর্তী সংস্করণে চালিত অ্যাপে সুপারিশ সরবরাহ করার জন্য, আপনার অ্যাপকে অবশ্যই সুপারিশ চ্যানেল ব্যবহার করতে হবে।

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

অ্যান্ড্রয়েড টিভির হোম স্ক্রিনে সুপারিশ সারির একটি উদাহরণ
চিত্র ১. সুপারিশ সারির একটি উদাহরণ।

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

সুপারিশের জন্য সর্বোত্তম অনুশীলন

সুপারিশগুলো ব্যবহারকারীদের তাদের পছন্দের কন্টেন্ট এবং অ্যাপগুলো দ্রুত খুঁজে পেতে সাহায্য করে। আপনার টিভি অ্যাপে একটি চমৎকার ব্যবহারকারীর অভিজ্ঞতা তৈরি করার জন্য, ব্যবহারকারীদের জন্য উন্নত মানের এবং প্রাসঙ্গিক সুপারিশ তৈরি করা একটি গুরুত্বপূর্ণ বিষয়। এই কারণে, আপনি ব্যবহারকারীকে কী ধরনের সুপারিশ দেখাবেন তা সতর্কতার সাথে বিবেচনা করা এবং সেগুলোকে নিবিড়ভাবে পরিচালনা করা উচিত।

সুপারিশের প্রকারভেদ

যখন আপনি সুপারিশ তৈরি করবেন, তখন ব্যবহারকারীদের অসমাপ্ত দেখার কার্যকলাপের সাথে লিঙ্ক করে দিন অথবা এমন কার্যকলাপের পরামর্শ দিন যা সেটিকে সম্পর্কিত বিষয়বস্তুতে প্রসারিত করে। এখানে কিছু নির্দিষ্ট ধরণের সুপারিশ দেওয়া হলো যা আপনি বিবেচনা করতে পারেন:

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

সর্বোত্তম ব্যবহারকারীর অভিজ্ঞতার জন্য কীভাবে সুপারিশ কার্ড ডিজাইন করতে হয় সে সম্পর্কে আরও তথ্যের জন্য, Android TV ডিজাইন স্পেক-এর Recommendation Row দেখুন।

সুপারিশগুলি রিফ্রেশ করুন

সুপারিশগুলো রিফ্রেশ করার সময়, শুধু সেগুলো মুছে ফেলে আবার পোস্ট করবেন না, কারণ এমনটা করলে সুপারিশগুলো তালিকার একেবারে শেষে চলে আসে। কোনো কন্টেন্ট, যেমন একটি সিনেমা, একবার প্লে করা হয়ে গেলে, সেটিকে সুপারিশ থেকে সরিয়ে দিন

সুপারিশগুলি কাস্টমাইজ করুন

আপনি কার্ডের ফোরগ্রাউন্ড ও ব্যাকগ্রাউন্ড ইমেজ, রঙ, অ্যাপ আইকন, টাইটেল এবং সাবটাইটেলের মতো ইউজার ইন্টারফেস এলিমেন্ট সেট করার মাধ্যমে ব্র্যান্ডিং তথ্য তুলে ধরতে রেকমেন্ডেশন কার্ডগুলো কাস্টমাইজ করতে পারেন। আরও জানতে, অ্যান্ড্রয়েড টিভি ডিজাইন স্পেক-এর রেকমেন্ডেশন রো দেখুন।

দলগত সুপারিশ

আপনি চাইলে সুপারিশের উৎসের উপর ভিত্তি করে সুপারিশগুলোকে গ্রুপ করতে পারেন। উদাহরণস্বরূপ, আপনার অ্যাপ দুই ধরনের সুপারিশ প্রদান করতে পারে: ব্যবহারকারী যে কন্টেন্টে সাবস্ক্রাইব করেছেন তার জন্য সুপারিশ, এবং নতুন ট্রেন্ডিং কন্টেন্টের জন্য সুপারিশ, যা সম্পর্কে ব্যবহারকারী হয়তো অবগত নন।

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

কোনো সুপারিশের গ্রুপ কী স্ট্রিং সেট করতে NotificationCompat.Builder.setGroup() ব্যবহার করুন। উদাহরণস্বরূপ, কোনো সুপারিশকে নতুন ট্রেন্ডিং কন্টেন্ট ধারণকারী একটি গ্রুপের অন্তর্ভুক্ত হিসেবে চিহ্নিত করতে, আপনি setGroup("trending") কল করতে পারেন।

একটি সুপারিশ পরিষেবা তৈরি করুন

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

নিম্নলিখিত কোড উদাহরণটি দেখায় কিভাবে আপনার অ্যাপ্লিকেশনের জন্য একটি সুপারিশ পরিষেবা তৈরি করতে IntentService প্রসারিত করা যায়:

কোটলিন

class UpdateRecommendationsService : IntentService("RecommendationService") {
    override protected fun onHandleIntent(intent: Intent) {
        Log.d(TAG, "Updating recommendation cards")
        val recommendations = VideoProvider.getMovieList()
        if (recommendations == null) return

        var count = 0

        try {
            val builder = RecommendationBuilder()
                    .setContext(applicationContext)
                    .setSmallIcon(R.drawable.videos_by_google_icon)

            for (entry in recommendations.entrySet()) {
                for (movie in entry.getValue()) {
                    Log.d(TAG, "Recommendation - " + movie.getTitle())

                    builder.setBackground(movie.getCardImageUrl())
                            .setId(count + 1)
                            .setPriority(MAX_RECOMMENDATIONS - count)
                            .setTitle(movie.getTitle())
                            .setDescription(getString(R.string.popular_header))
                            .setImage(movie.getCardImageUrl())
                            .setIntent(buildPendingIntent(movie))
                            .build()
                    if (++count >= MAX_RECOMMENDATIONS) {
                        break
                    }
                }
                if (++count >= MAX_RECOMMENDATIONS) {
                    break
                }
            }
        } catch (e: IOException) {
            Log.e(TAG, "Unable to update recommendation", e)
        }
    }

    private fun buildPendingIntent(movie: Movie): PendingIntent {
        val detailsIntent = Intent(this, DetailsActivity::class.java)
        detailsIntent.putExtra("Movie", movie)

        val stackBuilder = TaskStackBuilder.create(this)
        stackBuilder.addParentStack(DetailsActivity::class.java)
        stackBuilder.addNextIntent(detailsIntent)

        // Ensure a unique PendingIntents, otherwise all
        // recommendations end up with the same PendingIntent
        detailsIntent.setAction(movie.getId().toString())

        val intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
        return intent
    }

    companion object {
        private val TAG = "UpdateRecommendationsService"
        private val MAX_RECOMMENDATIONS = 3
    }
}

জাভা

public class UpdateRecommendationsService extends IntentService {
    private static final String TAG = "UpdateRecommendationsService";
    private static final int MAX_RECOMMENDATIONS = 3;

    public UpdateRecommendationsService() {
        super("RecommendationService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "Updating recommendation cards");
        HashMap<String, List<Movie>> recommendations = VideoProvider.getMovieList();
        if (recommendations == null) return;

        int count = 0;

        try {
            RecommendationBuilder builder = new RecommendationBuilder()
                    .setContext(getApplicationContext())
                    .setSmallIcon(R.drawable.videos_by_google_icon);

            for (Map.Entry<String, List<Movie>> entry : recommendations.entrySet()) {
                for (Movie movie : entry.getValue()) {
                    Log.d(TAG, "Recommendation - " + movie.getTitle());

                    builder.setBackground(movie.getCardImageUrl())
                            .setId(count + 1)
                            .setPriority(MAX_RECOMMENDATIONS - count)
                            .setTitle(movie.getTitle())
                            .setDescription(getString(R.string.popular_header))
                            .setImage(movie.getCardImageUrl())
                            .setIntent(buildPendingIntent(movie))
                            .build();

                    if (++count >= MAX_RECOMMENDATIONS) {
                        break;
                    }
                }
                if (++count >= MAX_RECOMMENDATIONS) {
                    break;
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "Unable to update recommendation", e);
        }
    }

    private PendingIntent buildPendingIntent(Movie movie) {
        Intent detailsIntent = new Intent(this, DetailsActivity.class);
        detailsIntent.putExtra("Movie", movie);

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(DetailsActivity.class);
        stackBuilder.addNextIntent(detailsIntent);
        // Ensure a unique PendingIntents, otherwise all
        // recommendations end up with the same PendingIntent
        detailsIntent.setAction(Long.toString(movie.getId()));

        PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
        return intent;
    }
}

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

<manifest ... >
  <application ... >
    ...

    <service
            android:name="com.example.android.tvleanback.UpdateRecommendationsService"
            android:enabled="true" />
  </application>
</manifest>

সুপারিশ তৈরি করুন

আপনার সুপারিশ পরিষেবাটি চালু হয়ে গেলে, এটিকে অবশ্যই সুপারিশ তৈরি করে অ্যান্ড্রয়েড ফ্রেমওয়ার্কের কাছে পাঠাতে হবে। ফ্রেমওয়ার্কটি এই সুপারিশগুলোকে Notification অবজেক্ট হিসেবে গ্রহণ করে, যেগুলো একটি নির্দিষ্ট টেমপ্লেট ব্যবহার করে এবং একটি নির্দিষ্ট ক্যাটাগরি দ্বারা চিহ্নিত থাকে।

মান নির্ধারণ করা

রেকমেন্ডেশন কার্ডের UI এলিমেন্টের মান সেট করার জন্য, আপনাকে নিচে বর্ণিত বিল্ডার প্যাটার্ন অনুসরণ করে একটি বিল্ডার ক্লাস তৈরি করতে হবে। প্রথমে, আপনাকে রেকমেন্ডেশন কার্ডের এলিমেন্টগুলোর মান সেট করতে হবে।

কোটলিন

class RecommendationBuilder {
    ...

    fun setTitle(title: String): RecommendationBuilder {
        this.title = title
        return this
    }

    fun setDescription(description: String): RecommendationBuilder {
        this.description = description
        return this
    }

    fun setImage(uri: String): RecommendationBuilder {
        imageUri = uri
        return this
    }

    fun setBackground(uri: String): RecommendationBuilder {
        backgroundUri = uri
        return this
    }

...

জাভা

public class RecommendationBuilder {
    ...

    public RecommendationBuilder setTitle(String title) {
            this.title = title;
            return this;
        }

        public RecommendationBuilder setDescription(String description) {
            this.description = description;
            return this;
        }

        public RecommendationBuilder setImage(String uri) {
            imageUri = uri;
            return this;
        }

        public RecommendationBuilder setBackground(String uri) {
            backgroundUri = uri;
            return this;
        }
...

বিজ্ঞপ্তি তৈরি করুন

একবার মানগুলো সেট করে ফেলার পর, আপনি নোটিফিকেশনটি বিল্ড করবেন, বিল্ডার ক্লাস থেকে মানগুলো নোটিফিকেশনে অ্যাসাইন করবেন এবং NotificationCompat.Builder.build() কল করবেন।

এছাড়াও, setLocalOnly() কল করতে ভুলবেন না, যাতে NotificationCompat.BigPictureStyle নোটিফিকেশনটি অন্য ডিভাইসে দেখা না যায়।

নিম্নলিখিত কোড উদাহরণটি দেখায় কিভাবে একটি সুপারিশ তৈরি করতে হয়।

কোটলিন

class RecommendationBuilder {
    ...

    @Throws(IOException::class)
    fun build(): Notification {
        ...

        val notification = NotificationCompat.BigPictureStyle(
        NotificationCompat.Builder(context)
                .setContentTitle(title)
                .setContentText(description)
                .setPriority(priority)
                .setLocalOnly(true)
                .setOngoing(true)
                .setColor(context.resources.getColor(R.color.fastlane_background))
                .setCategory(Notification.CATEGORY_RECOMMENDATION)
                .setLargeIcon(image)
                .setSmallIcon(smallIcon)
                .setContentIntent(intent)
                .setExtras(extras))
                .build()

        return notification
    }
}

জাভা

public class RecommendationBuilder {
    ...

    public Notification build() throws IOException {
        ...

        Notification notification = new NotificationCompat.BigPictureStyle(
                new NotificationCompat.Builder(context)
                        .setContentTitle(title)
                        .setContentText(description)
                        .setPriority(priority)
                        .setLocalOnly(true)
                        .setOngoing(true)
                        .setColor(context.getResources().getColor(R.color.fastlane_background))
                        .setCategory(Notification.CATEGORY_RECOMMENDATION)
                        .setLargeIcon(image)
                        .setSmallIcon(smallIcon)
                        .setContentIntent(intent)
                        .setExtras(extras))
                .build();

        return notification;
    }
}

সুপারিশ পরিষেবা চালান

বর্তমান সুপারিশগুলো তৈরি করার জন্য আপনার অ্যাপের সুপারিশ পরিষেবাটি অবশ্যই নির্দিষ্ট সময় পর পর চলতে হবে। আপনার পরিষেবাটি চালানোর জন্য, এমন একটি ক্লাস তৈরি করুন যা একটি টাইমার চালায় এবং নিয়মিত বিরতিতে সেটিকে কল করে। নিম্নলিখিত কোড উদাহরণটি প্রতি আধা ঘণ্টা পর পর একটি সুপারিশ পরিষেবা চালু করার জন্য BroadcastReceiver ক্লাসটিকে এক্সটেন্ড করে:

কোটলিন

class BootupActivity : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d(TAG, "BootupActivity initiated")
        if (intent.action.endsWith(Intent.ACTION_BOOT_COMPLETED)) {
            scheduleRecommendationUpdate(context)
        }
    }

    private fun scheduleRecommendationUpdate(context: Context) {
        Log.d(TAG, "Scheduling recommendations update")
        val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val recommendationIntent = Intent(context, UpdateRecommendationsService::class.java)
        val alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0)
        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                INITIAL_DELAY,
                AlarmManager.INTERVAL_HALF_HOUR,
                alarmIntent
        )
    }

    companion object {
        private val TAG = "BootupActivity"
        private val INITIAL_DELAY:Long = 5000
    }
}

জাভা

public class BootupActivity extends BroadcastReceiver {
    private static final String TAG = "BootupActivity";

    private static final long INITIAL_DELAY = 5000;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "BootupActivity initiated");
        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
            scheduleRecommendationUpdate(context);
        }
    }

    private void scheduleRecommendationUpdate(Context context) {
        Log.d(TAG, "Scheduling recommendations update");

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class);
        PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0);

        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                INITIAL_DELAY,
                AlarmManager.INTERVAL_HALF_HOUR,
                alarmIntent);
    }
}

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

<manifest ... >
  <application ... >
    <receiver android:name="com.example.android.tvleanback.BootupActivity"
              android:enabled="true"
              android:exported="false">
      <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
      </intent-filter>
    </receiver>
  </application>
</manifest>

গুরুত্বপূর্ণ: বুট সম্পন্ন হওয়ার নোটিফিকেশন পেতে হলে আপনার অ্যাপকে RECEIVE_BOOT_COMPLETED পারমিশনটির জন্য অনুরোধ করতে হবে। আরও তথ্যের জন্য, ACTION_BOOT_COMPLETED দেখুন।

আপনার রিকমেন্ডেশন সার্ভিস ক্লাসের onHandleIntent() মেথডে, ম্যানেজারের কাছে রিকমেন্ডেশনটি নিম্নরূপে পোস্ট করুন:

কোটলিন

val notification = notificationBuilder.build()
notificationManager.notify(id, notification)

জাভা

Notification notification = notificationBuilder.build();
notificationManager.notify(id, notification);
,

টিভি ব্যবহারের ক্ষেত্রে, ব্যবহারকারীরা সাধারণত কোনো কিছু দেখার আগে ন্যূনতম হস্তক্ষেপ করতে পছন্দ করেন। অনেক টিভি ব্যবহারকারীর জন্য আদর্শ পরিস্থিতি হলো: বসে পড়া, টিভি চালু করা এবং দেখা। ব্যবহারকারীরা যে কন্টেন্ট উপভোগ করেন, সেখানে পৌঁছানোর জন্য যে পথটিতে সবচেয়ে কম ধাপ থাকে, সাধারণত তারাই সেই পথটি বেছে নেন।

দ্রষ্টব্য: এখানে বর্ণিত API-গুলি শুধুমাত্র Android 7.1 (API লেভেল 25) পর্যন্ত সংস্করণে চালিত অ্যাপে সুপারিশ করার জন্য ব্যবহার করুন। Android 8.0 (API লেভেল 26) এবং তার পরবর্তী সংস্করণে চালিত অ্যাপে সুপারিশ সরবরাহ করার জন্য, আপনার অ্যাপকে অবশ্যই সুপারিশ চ্যানেল ব্যবহার করতে হবে।

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

অ্যান্ড্রয়েড টিভির হোম স্ক্রিনে সুপারিশ সারির একটি উদাহরণ
চিত্র ১. সুপারিশ সারির একটি উদাহরণ।

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

সুপারিশের জন্য সর্বোত্তম অনুশীলন

সুপারিশগুলো ব্যবহারকারীদের তাদের পছন্দের কন্টেন্ট এবং অ্যাপগুলো দ্রুত খুঁজে পেতে সাহায্য করে। আপনার টিভি অ্যাপে একটি চমৎকার ব্যবহারকারীর অভিজ্ঞতা তৈরি করার জন্য, ব্যবহারকারীদের জন্য উন্নত মানের এবং প্রাসঙ্গিক সুপারিশ তৈরি করা একটি গুরুত্বপূর্ণ বিষয়। এই কারণে, আপনি ব্যবহারকারীকে কী ধরনের সুপারিশ দেখাবেন তা সতর্কতার সাথে বিবেচনা করা এবং সেগুলোকে নিবিড়ভাবে পরিচালনা করা উচিত।

সুপারিশের প্রকারভেদ

যখন আপনি সুপারিশ তৈরি করবেন, তখন ব্যবহারকারীদের অসমাপ্ত দেখার কার্যকলাপের সাথে লিঙ্ক করে দিন অথবা এমন কার্যকলাপের পরামর্শ দিন যা সেটিকে সম্পর্কিত বিষয়বস্তুতে প্রসারিত করে। এখানে কিছু নির্দিষ্ট ধরণের সুপারিশ দেওয়া হলো যা আপনি বিবেচনা করতে পারেন:

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

সর্বোত্তম ব্যবহারকারীর অভিজ্ঞতার জন্য কীভাবে সুপারিশ কার্ড ডিজাইন করতে হয় সে সম্পর্কে আরও তথ্যের জন্য, Android TV ডিজাইন স্পেক-এর Recommendation Row দেখুন।

সুপারিশগুলি রিফ্রেশ করুন

সুপারিশগুলো রিফ্রেশ করার সময়, শুধু সেগুলো মুছে ফেলে আবার পোস্ট করবেন না, কারণ এমনটা করলে সুপারিশগুলো তালিকার একেবারে শেষে চলে আসে। কোনো কন্টেন্ট, যেমন একটি সিনেমা, একবার প্লে করা হয়ে গেলে, সেটিকে সুপারিশ থেকে সরিয়ে দিন

সুপারিশগুলি কাস্টমাইজ করুন

আপনি কার্ডের ফোরগ্রাউন্ড ও ব্যাকগ্রাউন্ড ইমেজ, রঙ, অ্যাপ আইকন, টাইটেল এবং সাবটাইটেলের মতো ইউজার ইন্টারফেস এলিমেন্ট সেট করার মাধ্যমে ব্র্যান্ডিং তথ্য তুলে ধরতে রেকমেন্ডেশন কার্ডগুলো কাস্টমাইজ করতে পারেন। আরও জানতে, অ্যান্ড্রয়েড টিভি ডিজাইন স্পেক-এর রেকমেন্ডেশন রো দেখুন।

দলগত সুপারিশ

আপনি চাইলে সুপারিশের উৎসের উপর ভিত্তি করে সুপারিশগুলোকে গ্রুপ করতে পারেন। উদাহরণস্বরূপ, আপনার অ্যাপ দুই ধরনের সুপারিশ প্রদান করতে পারে: ব্যবহারকারী যে কন্টেন্টে সাবস্ক্রাইব করেছেন তার জন্য সুপারিশ, এবং নতুন ট্রেন্ডিং কন্টেন্টের জন্য সুপারিশ, যা সম্পর্কে ব্যবহারকারী হয়তো অবগত নন।

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

কোনো সুপারিশের গ্রুপ কী স্ট্রিং সেট করতে NotificationCompat.Builder.setGroup() ব্যবহার করুন। উদাহরণস্বরূপ, কোনো সুপারিশকে নতুন ট্রেন্ডিং কন্টেন্ট ধারণকারী একটি গ্রুপের অন্তর্ভুক্ত হিসেবে চিহ্নিত করতে, আপনি setGroup("trending") কল করতে পারেন।

একটি সুপারিশ পরিষেবা তৈরি করুন

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

নিম্নলিখিত কোড উদাহরণটি দেখায় কিভাবে আপনার অ্যাপ্লিকেশনের জন্য একটি সুপারিশ পরিষেবা তৈরি করতে IntentService প্রসারিত করা যায়:

কোটলিন

class UpdateRecommendationsService : IntentService("RecommendationService") {
    override protected fun onHandleIntent(intent: Intent) {
        Log.d(TAG, "Updating recommendation cards")
        val recommendations = VideoProvider.getMovieList()
        if (recommendations == null) return

        var count = 0

        try {
            val builder = RecommendationBuilder()
                    .setContext(applicationContext)
                    .setSmallIcon(R.drawable.videos_by_google_icon)

            for (entry in recommendations.entrySet()) {
                for (movie in entry.getValue()) {
                    Log.d(TAG, "Recommendation - " + movie.getTitle())

                    builder.setBackground(movie.getCardImageUrl())
                            .setId(count + 1)
                            .setPriority(MAX_RECOMMENDATIONS - count)
                            .setTitle(movie.getTitle())
                            .setDescription(getString(R.string.popular_header))
                            .setImage(movie.getCardImageUrl())
                            .setIntent(buildPendingIntent(movie))
                            .build()
                    if (++count >= MAX_RECOMMENDATIONS) {
                        break
                    }
                }
                if (++count >= MAX_RECOMMENDATIONS) {
                    break
                }
            }
        } catch (e: IOException) {
            Log.e(TAG, "Unable to update recommendation", e)
        }
    }

    private fun buildPendingIntent(movie: Movie): PendingIntent {
        val detailsIntent = Intent(this, DetailsActivity::class.java)
        detailsIntent.putExtra("Movie", movie)

        val stackBuilder = TaskStackBuilder.create(this)
        stackBuilder.addParentStack(DetailsActivity::class.java)
        stackBuilder.addNextIntent(detailsIntent)

        // Ensure a unique PendingIntents, otherwise all
        // recommendations end up with the same PendingIntent
        detailsIntent.setAction(movie.getId().toString())

        val intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
        return intent
    }

    companion object {
        private val TAG = "UpdateRecommendationsService"
        private val MAX_RECOMMENDATIONS = 3
    }
}

জাভা

public class UpdateRecommendationsService extends IntentService {
    private static final String TAG = "UpdateRecommendationsService";
    private static final int MAX_RECOMMENDATIONS = 3;

    public UpdateRecommendationsService() {
        super("RecommendationService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "Updating recommendation cards");
        HashMap<String, List<Movie>> recommendations = VideoProvider.getMovieList();
        if (recommendations == null) return;

        int count = 0;

        try {
            RecommendationBuilder builder = new RecommendationBuilder()
                    .setContext(getApplicationContext())
                    .setSmallIcon(R.drawable.videos_by_google_icon);

            for (Map.Entry<String, List<Movie>> entry : recommendations.entrySet()) {
                for (Movie movie : entry.getValue()) {
                    Log.d(TAG, "Recommendation - " + movie.getTitle());

                    builder.setBackground(movie.getCardImageUrl())
                            .setId(count + 1)
                            .setPriority(MAX_RECOMMENDATIONS - count)
                            .setTitle(movie.getTitle())
                            .setDescription(getString(R.string.popular_header))
                            .setImage(movie.getCardImageUrl())
                            .setIntent(buildPendingIntent(movie))
                            .build();

                    if (++count >= MAX_RECOMMENDATIONS) {
                        break;
                    }
                }
                if (++count >= MAX_RECOMMENDATIONS) {
                    break;
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "Unable to update recommendation", e);
        }
    }

    private PendingIntent buildPendingIntent(Movie movie) {
        Intent detailsIntent = new Intent(this, DetailsActivity.class);
        detailsIntent.putExtra("Movie", movie);

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(DetailsActivity.class);
        stackBuilder.addNextIntent(detailsIntent);
        // Ensure a unique PendingIntents, otherwise all
        // recommendations end up with the same PendingIntent
        detailsIntent.setAction(Long.toString(movie.getId()));

        PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
        return intent;
    }
}

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

<manifest ... >
  <application ... >
    ...

    <service
            android:name="com.example.android.tvleanback.UpdateRecommendationsService"
            android:enabled="true" />
  </application>
</manifest>

সুপারিশ তৈরি করুন

আপনার সুপারিশ পরিষেবাটি চালু হয়ে গেলে, এটিকে অবশ্যই সুপারিশ তৈরি করে অ্যান্ড্রয়েড ফ্রেমওয়ার্কের কাছে পাঠাতে হবে। ফ্রেমওয়ার্কটি এই সুপারিশগুলোকে Notification অবজেক্ট হিসেবে গ্রহণ করে, যেগুলো একটি নির্দিষ্ট টেমপ্লেট ব্যবহার করে এবং একটি নির্দিষ্ট ক্যাটাগরি দ্বারা চিহ্নিত থাকে।

মান নির্ধারণ করা

রেকমেন্ডেশন কার্ডের UI এলিমেন্টের মান সেট করার জন্য, আপনাকে নিচে বর্ণিত বিল্ডার প্যাটার্ন অনুসরণ করে একটি বিল্ডার ক্লাস তৈরি করতে হবে। প্রথমে, আপনাকে রেকমেন্ডেশন কার্ডের এলিমেন্টগুলোর মান সেট করতে হবে।

কোটলিন

class RecommendationBuilder {
    ...

    fun setTitle(title: String): RecommendationBuilder {
        this.title = title
        return this
    }

    fun setDescription(description: String): RecommendationBuilder {
        this.description = description
        return this
    }

    fun setImage(uri: String): RecommendationBuilder {
        imageUri = uri
        return this
    }

    fun setBackground(uri: String): RecommendationBuilder {
        backgroundUri = uri
        return this
    }

...

জাভা

public class RecommendationBuilder {
    ...

    public RecommendationBuilder setTitle(String title) {
            this.title = title;
            return this;
        }

        public RecommendationBuilder setDescription(String description) {
            this.description = description;
            return this;
        }

        public RecommendationBuilder setImage(String uri) {
            imageUri = uri;
            return this;
        }

        public RecommendationBuilder setBackground(String uri) {
            backgroundUri = uri;
            return this;
        }
...

বিজ্ঞপ্তি তৈরি করুন

একবার মানগুলো সেট করে ফেলার পর, আপনি নোটিফিকেশনটি বিল্ড করবেন, বিল্ডার ক্লাস থেকে মানগুলো নোটিফিকেশনে অ্যাসাইন করবেন এবং NotificationCompat.Builder.build() কল করবেন।

এছাড়াও, setLocalOnly() কল করতে ভুলবেন না, যাতে NotificationCompat.BigPictureStyle নোটিফিকেশনটি অন্য ডিভাইসে দেখা না যায়।

নিম্নলিখিত কোড উদাহরণটি দেখায় কিভাবে একটি সুপারিশ তৈরি করতে হয়।

কোটলিন

class RecommendationBuilder {
    ...

    @Throws(IOException::class)
    fun build(): Notification {
        ...

        val notification = NotificationCompat.BigPictureStyle(
        NotificationCompat.Builder(context)
                .setContentTitle(title)
                .setContentText(description)
                .setPriority(priority)
                .setLocalOnly(true)
                .setOngoing(true)
                .setColor(context.resources.getColor(R.color.fastlane_background))
                .setCategory(Notification.CATEGORY_RECOMMENDATION)
                .setLargeIcon(image)
                .setSmallIcon(smallIcon)
                .setContentIntent(intent)
                .setExtras(extras))
                .build()

        return notification
    }
}

জাভা

public class RecommendationBuilder {
    ...

    public Notification build() throws IOException {
        ...

        Notification notification = new NotificationCompat.BigPictureStyle(
                new NotificationCompat.Builder(context)
                        .setContentTitle(title)
                        .setContentText(description)
                        .setPriority(priority)
                        .setLocalOnly(true)
                        .setOngoing(true)
                        .setColor(context.getResources().getColor(R.color.fastlane_background))
                        .setCategory(Notification.CATEGORY_RECOMMENDATION)
                        .setLargeIcon(image)
                        .setSmallIcon(smallIcon)
                        .setContentIntent(intent)
                        .setExtras(extras))
                .build();

        return notification;
    }
}

সুপারিশ পরিষেবা চালান

বর্তমান সুপারিশগুলো তৈরি করার জন্য আপনার অ্যাপের সুপারিশ পরিষেবাটি অবশ্যই নির্দিষ্ট সময় পর পর চলতে হবে। আপনার পরিষেবাটি চালানোর জন্য, এমন একটি ক্লাস তৈরি করুন যা একটি টাইমার চালায় এবং নিয়মিত বিরতিতে সেটিকে কল করে। নিম্নলিখিত কোড উদাহরণটি প্রতি আধা ঘণ্টা পর পর একটি সুপারিশ পরিষেবা চালু করার জন্য BroadcastReceiver ক্লাসটিকে এক্সটেন্ড করে:

কোটলিন

class BootupActivity : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d(TAG, "BootupActivity initiated")
        if (intent.action.endsWith(Intent.ACTION_BOOT_COMPLETED)) {
            scheduleRecommendationUpdate(context)
        }
    }

    private fun scheduleRecommendationUpdate(context: Context) {
        Log.d(TAG, "Scheduling recommendations update")
        val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val recommendationIntent = Intent(context, UpdateRecommendationsService::class.java)
        val alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0)
        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                INITIAL_DELAY,
                AlarmManager.INTERVAL_HALF_HOUR,
                alarmIntent
        )
    }

    companion object {
        private val TAG = "BootupActivity"
        private val INITIAL_DELAY:Long = 5000
    }
}

জাভা

public class BootupActivity extends BroadcastReceiver {
    private static final String TAG = "BootupActivity";

    private static final long INITIAL_DELAY = 5000;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "BootupActivity initiated");
        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
            scheduleRecommendationUpdate(context);
        }
    }

    private void scheduleRecommendationUpdate(Context context) {
        Log.d(TAG, "Scheduling recommendations update");

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class);
        PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0);

        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                INITIAL_DELAY,
                AlarmManager.INTERVAL_HALF_HOUR,
                alarmIntent);
    }
}

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

<manifest ... >
  <application ... >
    <receiver android:name="com.example.android.tvleanback.BootupActivity"
              android:enabled="true"
              android:exported="false">
      <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
      </intent-filter>
    </receiver>
  </application>
</manifest>

গুরুত্বপূর্ণ: বুট সম্পন্ন হওয়ার নোটিফিকেশন পেতে হলে আপনার অ্যাপকে RECEIVE_BOOT_COMPLETED পারমিশনটির জন্য অনুরোধ করতে হবে। আরও তথ্যের জন্য, ACTION_BOOT_COMPLETED দেখুন।

আপনার রিকমেন্ডেশন সার্ভিস ক্লাসের onHandleIntent() মেথডে, ম্যানেজারের কাছে রিকমেন্ডেশনটি নিম্নরূপে পোস্ট করুন:

কোটলিন

val notification = notificationBuilder.build()
notificationManager.notify(id, notification)

জাভা

Notification notification = notificationBuilder.build();
notificationManager.notify(id, notification);