الاقتراحات في Android N والإصدارات الأقدم

عند التفاعل مع أجهزة التلفزيون، يفضّل المستخدمون عمومًا إبداء الحدّ الأدنى من المعلومات قبل مشاهدة المحتوى. المحتوى. والسيناريو المثالي للعديد من مستخدمي التلفزيون هو: الجلوس وتشغيل التلفزيون والمشاهدة. أقل عدد من الخطوات لإقناع المستخدمين بالمحتوى الذي يهمّهم هو المسار الذي يفضّلونه.

ملاحظة: يمكنك استخدام واجهات برمجة التطبيقات الموضّحة هنا لتقديم اقتراحات. في التطبيقات التي تعمل بإصدارات Android بما في ذلك الإصدار 7.1 من Android (المستوى 25 من واجهة برمجة التطبيقات) فقط. للإمداد اقتراحات للتطبيقات التي تعمل بنظام التشغيل Android 8.0 (المستوى 26 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يجب أن يستخدم تطبيقك قنوات الاقتراحات

يساعد إطار عمل Android في الوصول إلى الحد الأدنى من تفاعل الإدخال من خلال توفير صف للاقتراحات. على الشاشة الرئيسية. تظهر اقتراحات المحتوى في الصف الأول من شاشة التلفزيون الرئيسية بعد أول استخدام للجهاز. يمكنك المساهمة بالاقتراحات من كتالوج محتوى تطبيقك لجذب المستخدمين مرة أخرى إلى تطبيقك.

الشكل 1. مثال على صف الاقتراحات

يوضِّح لك هذا الدليل كيفية إنشاء اقتراحات وتقديمها إلى إطار عمل Android. كي يتمكّن المستخدمون من اكتشاف محتوى تطبيقك والاستمتاع به بسهولة يمكنك أيضًا الاطّلاع على نموذج التنفيذ في الـ نموذج تطبيق Leanback .

أفضل الممارسات المتعلّقة بالاقتراحات

تساعد الاقتراحات المستخدمين في العثور بسرعة على المحتوى والتطبيقات التي يستمتعون بها. قيد الإنشاء تعد التوصيات عالية الجودة وذات الصلة بالمستخدمين عاملاً مهمًا في إنشاء تجربة مستخدم رائعة مع تطبيق التلفزيون الخاص بك. لهذا السبب، يجب أن تفكر مليًا في ما والتوصيات التي تقدمها إلى المستخدم وتديرها عن كثب.

أنواع الاقتراحات

عند إنشاء اقتراحات، يجب ربط المستخدمين مرة أخرى بأنشطة مشاهدة غير مكتملة أو واقتراح أنشطة تشمل ذلك المحتوى ذي الصلة. فيما يلي بعض الأنواع المحددة من والتوصيات التي يجب مراعاتها:

  • اقتراحات محتوى لمواصلة العرض في الحلقة التالية لكي يستأنف المستخدمون العمل عليها يشاهدون سلسلة ما. يمكنك أيضًا استخدام اقتراحات حول متابعة الأفلام أو البرامج التلفزيونية أو ملفات البودكاست التي تم إيقافها مؤقتًا. ليتمكّن المستخدمون من العودة إلى مشاهدة المحتوى المتوقّف مؤقتًا ببضع نقرات فقط.
  • اقتراحات المحتوى الجديد، مثل اقتراح حلقة جديدة يتم عرضها لأول مرة، إذا رأى المستخدم من مشاهدة مسلسل آخر. بالإضافة إلى ذلك، إذا كان تطبيقك يتيح للمستخدمين الاشتراك في المحتوى أو متابعته أو تتبّعه المحتوى، واستخدام اقتراحات المحتوى الجديدة للعناصر التي لم تتم مشاهدتها في قائمة المحتوى الذي يتم تتبعه.
  • اقتراحات المحتوى ذو الصلة استنادًا إلى اقتراحات المستخدمين سلوك المشاهدة السابق

لمزيد من المعلومات حول كيفية تصميم بطاقات اقتراحات لتقديم أفضل تجربة للمستخدم، يُرجى الاطّلاع على صف الاقتراحات في مواصفات تصميم Android TV.

إعادة تحميل المحتوى المقترح

عند إعادة تحميل الاقتراحات، لا تزيلها وتعيد نشرها فحسب، لأنّ ذلك يؤدي إلى الاقتراحات التي ستظهر في نهاية صف الاقتراحات بمجرد عنصر المحتوى، مثل الفيلم، تم تشغيله، إزالته من الاقتراحات.

تخصيص الاقتراحات

يمكنك تخصيص بطاقات الاقتراحات لنقل معلومات العلامة التجارية من خلال ضبط واجهة المستخدم. العناصر مثل واجهة البطاقة وصورة الخلفية واللون ورمز التطبيق والعنوان والعنوان الفرعي لمزيد من المعلومات، يُرجى مراجعة صف الاقتراحات في مواصفات تصميم Android TV.

اقتراحات المجموعة

يمكنك اختياريًا تجميع الاقتراحات استنادًا إلى مصدر الاقتراح. على سبيل المثال، يحبّذ تطبيقك مجموعتين من الاقتراحات: اقتراحات للمحتوى الذي يشترك فيه المستخدم، واقتراحات لمحتوى رائج جديد قد لا يكون المستخدم على عِلم به

يرتب النظام ويطلب التوصيات لكل مجموعة بشكل منفصل عند إنشاء أو تحديث صف التوصية. من خلال توفير معلومات المجموعة لاقتراحاتك، يمكنك ضمان بحيث لا يتم ترتيب توصياتك أسفل التوصيات غير ذات الصلة.

استخدام NotificationCompat.Builder.setGroup() لضبط سلسلة مفتاح المجموعة لاقتراح ما. بالنسبة لوضع علامة على الاقتراح على أنّه تابع لمجموعة تتضمن محتوًى رائجًا جديدًا يمكنك الاتصال بـ setGroup("trending").

إنشاء خدمة اقتراحات

يتم إنشاء اقتراحات المحتوى مع المعالجة في الخلفية. لكي يتمكن تطبيقك في الاقتراحات، وإنشاء خدمة تضيف بشكل دوري بطاقات بيانات من التطبيق إلى قائمة اقتراحات النظام.

يوضح مثال الرمز التالي كيفية توسيع IntentService إلى إنشاء خدمة اقتراحات لتطبيقك:

Kotlin

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

Java

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>

تقديم الاقتراحات

بعد بدء تشغيل خدمة التوصية، يجب أن تنشئ توصيات وتمررها إلى إطار عمل Android. يتلقّى إطار العمل الاقتراحات على شكل عناصر Notification تستخدم نموذجًا معيّنًا ويتم وضع علامة عليها .

تعيين القيم

لضبط قيم عناصر واجهة المستخدم لبطاقة الاقتراح، يمكنك إنشاء فئة أداة إنشاء تتبع نمط منصة الإنشاء الموصوف على النحو التالي. عليك أولاً تحديد قيم بطاقة الاقتراحات عناصر.

Kotlin

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
    }

...

Java

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". على أجهزة أخرى.

يوضح مثال التعليمة البرمجية التالي كيفية وضع توصية.

Kotlin

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

Java

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 لبدء التنفيذ الدوري لخدمة الاقتراحات. كل نصف ساعة:

Kotlin

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

Java

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() انشر التوصية إلى المدير على النحو التالي:

Kotlin

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

Java

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