تطبيقات قيد التشغيل دائمًا وميزة "وضع الإضاءة السينمائية"

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

إنّ إبقاء التطبيق مرئيًا باستمرار يؤثر بشكل كبير في عمر البطارية، لذا يجب مراعاة تأثير استهلاك الطاقة عند إضافة هذه الميزة.

المفاهيم الرئيسية

عند عرض تطبيق Wear OS على الشاشة بالكامل، يكون في إحدى حالتين من حالات الطاقة:

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

يتحكّم نظام التشغيل في الانتقال بين هذه الحالات.

التطبيق الذي يعمل دائمًا هو تطبيق يعرض المحتوى في كل من وضعَي التفاعلي والخلفية.

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

عمليات الانتقال في النظام والسلوك التلقائي

عندما يكون التطبيق في المقدّمة، يدير النظام عمليات الانتقال بين حالات الطاقة استنادًا إلى مهلتَين يتم تشغيلهما عند توقّف نشاط المستخدم.

  • المهلة رقم 1: من الحالة التفاعلية إلى حالة العرض المحيط: بعد فترة من عدم نشاط المستخدم، ينتقل الجهاز إلى حالة العرض المحيط.
  • المهلة رقم 2: الرجوع إلى خلفية شاشة الساعة: بعد فترة أخرى من عدم النشاط، قد يخفي النظام التطبيق الحالي ويعرض خلفية شاشة الساعة.

بعد أن ينتقل النظام إلى حالة الوضع المحيط للمرة الأولى، يعتمد السلوك التلقائي على إصدار Wear OS وإعدادات تطبيقك:

  • على الإصدار 5 من Wear OS والإصدارات الأقدم، يعرض النظام لقطة شاشة مشوّشة لتطبيقك المتوقف مؤقتًا، مع عرض الوقت في الأعلى. يتم تمثيل هذه الحالة من خلال عقدة "شاشة قفل بسيطة" في مخطط سير العمل التالي.
  • في الإصدار 6 من Wear OS والإصدارات الأحدث، إذا كان التطبيق يستهدف الإصدار 36 من حزمة تطوير البرامج (SDK) أو إصدارًا أحدث، سيتم اعتباره قيد التشغيل دائمًا. يتم تعتيم الشاشة، ولكن يظل التطبيق قيد التشغيل ومرئيًا. (قد تكون التحديثات غير متكررة، أي مرة واحدة في الدقيقة). يتم تمثيل هذه الحالة من خلال عقدة "التنشيط الدائم على مستوى العالم" في مخطط التدفق التالي.

تخصيص السلوك في حالة "الوضع المحيط"

بغض النظر عن السلوك التلقائي للنظام، يمكنك في جميع إصدارات Wear OS تخصيص مظهر تطبيقك أو سلوكه أثناء حالة الوضع المحيط باستخدام LocalAmbientModeManager لمراقبة عمليات الانتقال بين الحالات في Jetpack Compose على Wear OS. يتم تمثيل هذه الحالة من خلال عقدة "وضع Ambiactive" في مخطط التدفق التالي.

مراقبة حالة الاستراحة

للتفاعل مع أحداث "وضع الإضاءة السينمائية" في Jetpack Compose على Wear OS، اتّبِع الخطوات التالية:

  1. استخدِم LocalAmbientModeManager لمراقبة التغييرات في حالة "الوضع المحيط" في النظام، مع تعديل العناصر القابلة للإنشاء وفقًا لذلك. اضبط إعدادات المكوّن ليتوافق مع وضع استهلاك الطاقة المنخفضة في الحالة AmbientMode.Ambient، واستعادة العرض التفاعلي الكامل في الحالة AmbientMode.Interactive.

    // In a production application, the AmbientModeManager should be instantiated and provided at
    // the highest level of the Compose hierarchy (typically in the host Activity's setContent
    // block) using a CompositionLocalProvider. This ensures proper lifecycle management and
    // broad accessibility.
    
    // For this self-contained demo, AmbientModeManager is created and provided locally:
    val activityAmbientModeManager = rememberAmbientModeManager()
    CompositionLocalProvider(LocalAmbientModeManager provides activityAmbientModeManager) {
        val ambientModeManager = LocalAmbientModeManager.current
        val ambientMode = ambientModeManager?.currentAmbientMode
    
        if (ambientModeManager != null) {
            ambientModeManager.AmbientTickEffect {
                // While device is in ambient mode, update properties every minute or so
                // ...
            }
        }
    
        // ...
            val ambientModeName =
                when (ambientMode) {
                    is AmbientMode.Interactive -> "Interactive"
                    is AmbientMode.Ambient -> "Ambient"
                    else -> "Unknown"
                }
    
            Text(text = "$ambientModeName Mode")
            // ...
    }

المخطط الانسيابي للسلوك المحيط

يوضّح مخطط التدفق التالي كيف يحدّد النظام سلوك وضع العرض المحيط استنادًا إلى إصدار Wear OS على الجهاز، وtargetSdkVersion في تطبيقك، وما إذا كان يراقب عمليات الانتقال بين حالات وضع العرض المحيط (باستخدام LocalAmbientModeManager أو AmbientLifecycleCallback).

مخطط انسيابي يوضّح منطق القرار لوضع الاستراحة في Wear OS ويوضّح كيف يحدّد إصدار نظام التشغيل على الجهاز وإعدادات التطبيق إحدى النتائج الثلاث التالية: تراكب مموّه أو ميزة تشغيل الشاشة دائمًا (AOD) العالمية أو الوضع التكيّفي الذي يديره التطبيق.
الشكل 1.: مخطط انسيابي يوضّح منطق اتخاذ القرار في ما يتعلّق بوضع الاستراحة على Wear OS

التحكّم في مدة ظهور الشاشة

توضّح الأقسام التالية كيفية إدارة المدة التي يظل فيها تطبيقك معروضًا على الشاشة.

منع الرجوع إلى خلفية شاشة الساعة باستخدام "نشاط مستمر" أو "تحديث فوري"

بعد فترة من الوقت في حالة الوضع المحيط (المهلة رقم 2)، سيعود النظام عادةً إلى خلفية شاشة الساعة. يمكن للمستخدم ضبط مدة المهلة في إعدادات النظام. في بعض حالات الاستخدام، مثل تتبُّع المستخدم لتمرين رياضي، قد يحتاج التطبيق إلى البقاء مرئيًا لفترة أطول.

في الإصدار 5 من Wear OS والإصدارات الأحدث، يمكنك منع ذلك من خلال تنفيذ نشاط مستمر. إذا كان تطبيقك يعرض معلومات حول مهمة مستخدم مستمرة، مثل جلسة تمرين، يمكنك استخدام Ongoing Activity API لإبقاء تطبيقك مرئيًا إلى أن تنتهي المهمة. إذا عاد المستخدم يدويًا إلى خلفية شاشة الساعة، يوفّر مؤشر النشاط المستمر في الخلفية طريقة بنقرة واحدة للعودة إلى تطبيقك.

بدلاً من ذلك، يمكنك استخدام تحديث مباشر على الإصدار 7 من Wear OS والإصدارات الأحدث بدلاً من "نشاط جارٍ". لضمان التوافق مع الإصدارات القديمة، واصِل توفير ميزة "النشاط قيد التقدّم" على الأجهزة التي تعمل بالإصدار 6 أو الإصدارات الأقدم من نظام التشغيل Wear OS.

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

val activityIntent =
    Intent(this, AlwaysOnActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
    }

val pendingIntent =
    PendingIntent.getActivity(
        this,
        0,
        activityIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
    )

val notificationBuilder =
    NotificationCompat.Builder(this, CHANNEL_ID)
        // ...
        // ...
        .setOngoing(true)

// ...

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // ...
        // ...
        .setTouchIntent(pendingIntent)
        .build()

ongoingActivity.apply(applicationContext)

val notification = notificationBuilder.build()

إبقاء الشاشة مفعّلة ومنع حالة "العرض المحيط"

في حالات نادرة، قد تحتاج إلى منع الجهاز تمامًا من الانتقال إلى حالة وضع الاستراحة. أي لتجنُّب الخطأ Timeout #1. لإجراء ذلك، يمكنك استخدام المعدِّل keepScreenOn. تعمل هذه الميزة كقفل التنشيط، ما يحافظ على الجهاز في حالة تفاعلية. يجب استخدام هذا الخيار بحذر شديد لأنّه يؤثر بشكل كبير في عمر البطارية.

اقتراحات بشأن "وضع الاستراحة"

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

  • تقليل الفوضى البصرية وعرض الطاقة تشير واجهة المستخدم النظيفة والبسيطة إلى أنّ التطبيق في وضع توفير الطاقة، كما أنّها توفّر شحن البطارية بشكل كبير من خلال الحدّ من وحدات البكسل الساطعة.
    • إبقاء% 85 على الأقل من الشاشة باللون الأسود (نسبة البكسل النشط% 15 أو أقل)
    • اعرض المعلومات الأكثر أهمية فقط، وانقل التفاصيل الثانوية إلى الشاشة التفاعلية.
    • استخدِموا خطوطًا خارجية للرموز أو الأزرار الكبيرة بدلاً من التعبئة الكاملة، واضبطوا تباين الخطوط الخارجية لكي تظل العناصر قابلة للقراءة بدون التسبب في وميض الشاشة أو احتراقها.
    • تجنَّب استخدام مساحات كبيرة من الألوان الثابتة أو العلامات التجارية أو صور الخلفية غير الوظيفية.
  • التعامل مع البيانات الديناميكية والرسوم المتحركة القديمة
    • يتم استدعاء دالة AmbientTickEffect بشكل دوري فقط، أي مرة واحدة في الدقيقة عادةً، للحفاظ على الطاقة. وبسبب هذا القيد، تصبح أي بيانات تتغيّر بشكل متكرّر، مثل ساعة الإيقاف أو معدّل نبضات القلب أو مسافة التمرين، قديمة بين عمليات التحديث. لتجنُّب عرض معلومات مضلّلة وغير صحيحة، استبدِل هذه القيم المباشرة بمحتوى عنصر نائب ثابت، مثل --.
    • أزِل مؤشرات التقدّم التي تتطلّب تحديثات متكرّرة لتكون مفيدة للمستخدم في سياقه أو حالته الحالية، مثل حلقات التقدّم لجلسات الوسائط القصيرة. يمكنك الاحتفاظ بمؤشرات التقدم التي لا تزال توفّر قيمة عند تعديلها مرة واحدة في الدقيقة، مثل الموقّتات الطويلة.
    • إيقاف جميع الصور المتحركة مؤقتًا أو إزالتها في الوضع المحيط، بما في ذلك مؤشرات التقدّم المتحركة والنص المتحرّك
  • الحفاظ على تنسيق متّسق
    • احتفِظ بموضع العناصر نفسه في كل من الوضعَين التفاعلي والمحيطي لإنشاء انتقال سلس. يمكن أن تظل عناصر واجهة المستخدم التفاعلية، مثل الأزرار، مرئية لأنّ أجهزة Wear OS الحديثة تتيح إدخال البيانات باللمس أثناء جلسات وضع الاستراحة.
    • عرض الوقت الحالي دائمًا في Jetpack Compose لنظام التشغيل Wear OS، TimeText (المضمّن تلقائيًا في ScreenScaffold) هو متوافق مع وضع العرض المحيطي ويتم تعديله تلقائيًا مرة واحدة في الدقيقة في وضع العرض المحيطي.
  • الاستجابة للسياق
    • إذا كان المستخدم يتصفّح شاشة إعدادات أو ضبط عند انتقال الجهاز إلى وضع الاستراحة، ننصحك بعرض شاشة أكثر ملاءمةً من تطبيقك بدلاً من عرض شاشة الإعدادات.
  • التعامل مع المتطلبات الخاصة بالجهاز
    • عندما تعرض الدالة currentAmbientMode القيمة AmbientMode.Ambient:
      • إذا كانت قيمة isLowBitAmbientSupported هي true، يعني ذلك أنّ الشاشة تتوافق مع عدد أقل من البتات لكل لون في الوضع المحيط. أوقِف ميزة "تنعيم الحواف" (على سبيل المثال، في عمليات الرسم المخصّصة Canvas أو Paint) وتجنَّب تدرّجات الألوان حيثما أمكن ذلك.
      • إذا كانت قيمة isBurnInProtectionRequired هي true، ينقل النظام محتوى الشاشة بشكل دوري لمنع احتراق وحدات البكسل. لا تحرِّك عناصر واجهة المستخدم في تطبيقك يدويًا، بل تجنَّب المساحات البيضاء الصلبة وضَع عناصر واجهة المستخدم المهمة على بُعد 10 وحدات بكسل على الأقل من حافة الشاشة حتى لا يتم اقتصاصها عندما ينقل النظام الشاشة. (توفّر تنسيقات Wear Compose العادية، مثل ScreenScaffold، مساحة كافية للحواف.)

تصحيح الأخطاء والاختبار

قد تكون أوامر adb هذه مفيدة عند تطوير أو اختبار طريقة عمل تطبيقك عندما يكون الجهاز في وضع العرض المحيط:

# put device in ambient mode if the always on display is enabled in settings
# (and not disabled by other settings, such as theatre mode)
$ adb shell input keyevent KEYCODE_SLEEP

# put device in interactive mode
$ adb shell input keyevent KEYCODE_WAKEUP

مثال: تطبيق تمارين رياضية

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

لتحقيق ذلك، على المطوّر اتّخاذ الخطوات التالية:

  1. استخدِم LocalAmbientModeManager للتعامل مع تغييرات واجهة المستخدم بين حالتَي تفاعلية ومحيطة، مثل تعتيم الشاشة وإزالة البيانات غير الأساسية.
  2. أنشئ تصميمًا جديدًا منخفض الطاقة لحالة الاستراحة يتّبع أفضل الممارسات.
  3. استخدِم Ongoing Activity API (أو التحديثات الفورية على Wear OS 7 والإصدارات الأحدث) طوال مدة التمرين لمنع النظام من العودة إلى خلفية شاشة الساعة.

للحصول على عملية تنفيذ كاملة، يمكنك الاطّلاع على نموذج التمرين الرياضي المستند إلى Compose على GitHub. يوضّح هذا النموذج أيضًا كيفية استخدام الدالة المركّبة LocalAmbientModeManager لتسهيل عملية التعامل مع وضع عدم النشاط في Compose.