تعدد المهام على التلفزيون

يقدّم Android 14 (المستوى 34 لواجهة برمجة التطبيقات) بعض التحسينات على واجهات برمجة التطبيقات نافذة ضمن النافذة (PiP) لإتاحة تعدُّد المهام. على الرغم من توفُّر ميزة "نافذة ضمن النافذة" في الإصدار Android 8.0 (المستوى 26 لواجهة برمجة التطبيقات)، إلا أنّها لم تكن متوافقة على نطاق واسع على Android TV ولم تكن متاحة على الإطلاق على Google TV قبل الإصدار Android 13. يستخدم "تعدد المهام" على التلفزيون وضع "نافذة ضمن النافذة" (PIP) للسماح بوجود تطبيقَين منفصلَين على الشاشة: أحدهما يتم تشغيله في وضع ملء الشاشة والآخر في وضع "نافذة ضمن النافذة". هناك متطلبات مختلفة للتطبيقات التي تعمل في أي من هذين الوضعين.

ويتمثل السلوك التلقائي في تثبيت تطبيق "نافذة ضمن النافذة" فوق التطبيق في وضع ملء الشاشة، تمامًا مثل السلوك العادي لميزة نافذة ضمن النافذة على جهاز Android.

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

تشغيل تطبيقك في وضع "نافذة ضمن النافذة"

بالنسبة إلى أجهزة التلفزيون التي تعمل بنظام التشغيل Android 14 (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يمكنك تشغيل التطبيق في وضع "نافذة ضمن النافذة" من خلال طلب enterPictureInPictureMode(). لا يتوفر وضع "نافذة ضمن النافذة" (PIP) على أجهزة التلفزيون التي تعمل بإصدارات سابقة من نظام التشغيل Android.

وفي ما يلي مثال على كيفية تنفيذ منطق الزر للدخول في وضع "نافذة ضمن النافذة":

Kotlin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    pictureInPictureButton.visibility =
        if (requireActivity().packageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
            pictureInPictureButton.setOnClickListener {
                val aspectRatio = Rational(view.width, view.height)
                val params = PictureInPictureParams.Builder()
                    .setAspectRatio(aspectRatio)
                    .build()
                val result = requireActivity().enterPictureInPictureMode(params)
            }
            View.VISIBLE
        } else {
            View.GONE
        }
}

Java

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if (requireActivity().getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
        pictureInPictureButton.setVisibility(View.VISIBLE);
        pictureInPictureButton.setOnClickListener(v -> {
            Rational aspectRatio = new Rational(view.getWidth(), view.getHeight());
            PictureInPictureParams params = new PictureInPictureParams.Builder()
                    .setAspectRatio(aspectRatio)
                    .setTitle("My Streaming App")
                    .setSubtitle("My On-Demand Content")
                    .build();
            Boolean result = requireActivity().enterPictureInPictureMode(params);
        });
    } else {
        pictureInPictureButton.setVisibility(View.GONE);
    }
}

لا تتم إضافة الإجراء إلا إذا كان الجهاز يحتوي على ميزة النظام FEATURE_PICTURE_IN_PICTURE. ويتم أيضًا ضبط نسبة العرض إلى الارتفاع في وضع "نافذة ضمن النافذة" (PIP) كي تتطابق مع نسبة العرض إلى الارتفاع للفيديو الذي يتم تشغيله.

احرص على إضافة عنوان وعنوان فرعي لمنح المستخدم معلومات حول الغرض من استخدام هذه الميزة بشكل عام.

التعاون مع التطبيقات التي يتم تشغيلها في وضع "نافذة ضمن النافذة"

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

الاحتفاظ بواجهات برمجة التطبيقات الواضحة

في بعض الحالات، قد يتراكب تطبيق "نافذة ضمن النافذة" على مكوِّنات مهمّة من واجهة المستخدم في تطبيق ملء الشاشة. وللحدّ من هذه المشكلة، تتوفّر واجهات برمجة تطبيقات واضحة يمكن للتطبيقات استخدامها لتحديد مكونات واجهة المستخدم المُهمّة التي يجب عدم تركيبها. يحاول النظام تلبية الطلبات لتجنب تغطية هذه المكونات من خلال إعادة تحديد موضع نافذة "نافذة ضمن النافذة".

Keep-واضح

لتحديد ضرورة عدم تركيب طريقة عرض، استخدِم preferKeepClear في تنسيق XML كما في المثال التالي:

<TextView
    android:id="@+id/important_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:preferKeepClear="true"
    android:text="@string/app_name"/>

يمكنك أيضًا إجراء ذلك آليًا باستخدام setPreferKeepClear():

Kotlin

private lateinit var binding: MyLayoutBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    binding = MyLayoutBinding.inflate(layoutInflater)
    setContentView(binding.root)
    binding.importantText.isPreferKeepClear = true
}

Java

private MyLayoutBinding binding;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    binding = MyLayoutBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());
    binding.importantText.setPreferKeepClear(true);
}

وفي بعض الأحيان، لا تحتاج إلى إبقاء View واضحًا، بل جزء فقط منه. ويمكن استخدام setPreferKeepClearRects() لتحديد مناطق View التي يجب عدم تركيبها. قد تحتوي واجهات المستخدم التي لا تستخدم View في الأصل، مثل Flutter وJetpack Compose و WebView، على أقسام فرعية تحتاج إلى إبقاء المناطق واضحة. ويمكن استخدام واجهة برمجة التطبيقات هذه في هذه الحالات.

أنواع الاستخدام

يجب أن يفصح تطبيقك عن سمة قيمة البيانات الوصفية للسمة com.google.android.tv.pip.category والتي تتوافق مع النوع الأساسي أو أنواع استخدام وضع "نافذة ضمن النافذة". ويجب أن تشير أي سمة <activity> تم ضبط android:supportsPictureInPicture="true" إلى هذه السمة باستخدام قيمة ذات صلة من الجدول أدناه.

لا يُسمح بأنواع الاستخدام التي لا تندرج ضمن أي من هذه الفئات، ولا سيما أيّ تشغيل لمحتوى الوسائط، في وضع "نافذة ضمن النافذة" على التلفزيون.

القيمة الوصف
"communication" حالات استخدام الاتصالات، مثل مكالمات الفيديو أو المكالمات الصوتية
"smartHome" عمليات الدمج في المنزل المزوّد بأجهزة ذكية، مثل أجراس الباب المتصلة أو أجهزة مراقبة الأطفال
"health" حالات الاستخدام الصحي، مثل تتبُّع مستوى اللياقة البدنية أو مراقبة الصحة
"ticker" حالات استخدام مؤشر الأداء، مثل نتائج المباريات الرياضية المباشرة أو الأخبار ومؤشرات الأسهم

يتم الفصل بين القيم المتعدّدة بشريط عمودي (|). على سبيل المثال:

<meta-data android:name="com.google.android.tv.pip.category" android:value="smartHome|health" />