إنشاء مربّعات مخصّصة للإعدادات السريعة لتطبيقك

"الإعدادات السريعة" هي مربّعات معروضة في لوحة "الإعدادات السريعة"، وتمثّل إجراءات يمكن للمستخدمين النقر عليها لإكمال المهام المتكرّرة بسرعة. يمكن لتطبيقك توفير مربّع مخصّص للمستخدمين من خلال فئة TileService ، واستخدام عنصر Tile لتتبُّع حالة المربّع. على سبيل المثال، يمكنك إنشاء مربّع يتيح للمستخدمين تفعيل شبكة VPN التي يوفّرها تطبيقك أو إيقافها.

لوحة "الإعدادات السريعة" مع تفعيل وإيقاف مربّع شبكة VPN
الشكل 1. لوحة "الإعدادات السريعة" مع تفعيل مربّع شبكة VPN وإيقافه

تحديد وقت إنشاء مربّع

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

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

حالات استخدام مربّعات تطبيقات اللياقة البدنية
الشكل 2. أمثلة على المربّعات المقترَحة وغير المقترَحة لتطبيق لياقة بدنية

للمساعدة في تحسين قابلية اكتشاف تطبيقك المصغّر وتسهيل استخدامه، ننصحك بتجنُّب الممارسات التالية:

  • تجنَّب استخدام المربّعات لتشغيل تطبيق، واستخدِم اختصار تطبيق أو مشغّل تطبيقات عادي بدلاً من ذلك.

  • تجنَّب استخدام المربّعات لإجراءات المستخدمين لمرة واحدة. استخدِم اختصار تطبيق أو إشعارًا بدلاً من ذلك.

  • تجنَّب إنشاء عدد كبير جدًا من المربّعات. ننصحك باستخدام اثنتين كحد أقصى لكل تطبيق، واستخدام اختصار تطبيق بدلاً من ذلك.

  • تجنَّب استخدام مربّعات تعرض معلومات ولكنّها غير تفاعلية للمستخدمين. يمكنك استخدام إشعار أو أداة بدلاً من ذلك.

إنشاء مربّع

لإنشاء مربّع، عليك أولاً إنشاء رمز مربّع مناسب، ثم إنشاء TileService والإشارة إليه في ملف بيان التطبيق.

يقدّم نموذج الإعدادات السريعة مثالاً على كيفية إنشاء مربّع وإدارته.

إنشاء رمز مخصّص

يجب توفير رمز مخصّص يظهر على المربّع في لوحة "الإعدادات السريعة". (ستضيف هذا الرمز عند الإفصاح عن TileService، كما هو موضّح في القسم التالي). يجب أن يكون الرمز باللون الأبيض الخالص مع خلفية شفافة، وأن يبلغ حجمه 24 × 24 وحدة بكسل مستقل الكثافة، وأن يكون بالتنسيق VectorDrawable.

مثال على متّجه قابل للرسم
الشكل 3. مثال على متّجه قابل للرسم

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

إنشاء TileService وتعريفه

أنشئ خدمة للوحة تتضمّن الفئة TileService.

Kotlin

class MyQSTileService: TileService() {

  // Called when the user adds your tile.
  override fun onTileAdded() {
    super.onTileAdded()
  }
  // Called when your app can update your tile.
  override fun onStartListening() {
    super.onStartListening()
  }

  // Called when your app can no longer update your tile.
  override fun onStopListening() {
    super.onStopListening()
  }

  // Called when the user taps on your tile in an active or inactive state.
  override fun onClick() {
    super.onClick()
  }
  // Called when the user removes your tile.
  override fun onTileRemoved() {
    super.onTileRemoved()
  }
}

Java

public class MyQSTileService extends TileService {

  // Called when the user adds your tile.
  @Override
  public void onTileAdded() {
    super.onTileAdded();
  }

  // Called when your app can update your tile.
  @Override
  public void onStartListening() {
    super.onStartListening();
  }

  // Called when your app can no longer update your tile.
  @Override
  public void onStopListening() {
    super.onStopListening();
  }

  // Called when the user taps on your tile in an active or inactive state.
  @Override
  public void onClick() {
    super.onClick();
  }

  // Called when the user removes your tile.
  @Override
  public void onTileRemoved() {
    super.onTileRemoved();
  }
}

عليك تعريف TileService في ملف بيان تطبيقك. أضِف اسم TileService وتصنيفه، والرمز المخصّص الذي أنشأته في القسم السابق، والإذن المناسب.

 <service
     android:name=".MyQSTileService"
     android:exported="true"
     android:label="@string/my_default_tile_label"  // 18-character limit.
     android:icon="@drawable/my_default_icon_label"
     android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
     <intent-filter>
         <action android:name="android.service.quicksettings.action.QS_TILE" />
     </intent-filter>
 </service>

إدارة TileService

بعد إنشاء TileService والإفصاح عنه في بيان تطبيقك، عليك إدارة حالته.

TileService هي خدمة مرتبطة. يتم ربط TileService عندما يطلب تطبيقك ذلك أو إذا كان النظام بحاجة إلى التواصل معه. يتضمّن عمر خدمة مرتبطة نموذجي أربع طرق ردّ اتصال على النحو التالي: onCreate() وonBind() وonUnbind() وonDestroy(). يتم استدعاء هذه الطرق من خلال النظام في كل مرة تدخل فيها الخدمة مرحلة جديدة من مراحل نشاطها.

نظرة عامة على مراحل نشاط TileService

بالإضافة إلى دوال الرجوع التي تتحكّم في دورة حياة الخدمة المرتبطة، يجب تنفيذ طرق أخرى خاصة بدورة حياة TileService. يمكن استدعاء هذه الطرق خارج onCreate() وonDestroy() لأنّ طرق دورة حياة Service وطرق دورة حياة TileService يتم استدعاؤها في سلسلتَي محادثات غير متزامنتَين منفصلتَين.

تحتوي دورة حياة TileService على الطرق التالية التي يستدعيها النظام في كل مرة يدخل فيها TileService مرحلة جديدة من مراحل دورة الحياة:

  • onTileAdded(): يتم استدعاء هذه الطريقة فقط عندما يضيف المستخدم مربّعك للمرة الأولى، وعندما يزيل المستخدم مربّعك ثم يعيد إضافته. هذا هو أفضل وقت لتنفيذ أي عملية تهيئة لمرة واحدة. ومع ذلك، قد لا يفي ذلك بجميع عمليات الإعداد المطلوبة.

  • onStartListening() وonStopListening(): يتم استدعاء هاتين الطريقتين كلما حدّث تطبيقك اللوحة، ويتم استدعاؤهما بشكل متكرر. يبقى TileService مرتبطًا بين onStartListening() و onStopListening()، ما يسمح لتطبيقك بتعديل اللوحة وإرسال التحديثات.

  • onTileRemoved(): لا يتم استدعاء هذه الطريقة إلا إذا أزال المستخدم المربّع.

اختيار وضع استماع

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

لا تفترض أنّ TileService سيتم عرضه خارج زوج الطريقتين onStartListening() وonStopListening().

استخدِم وضع النشاط لـ TileService يستمع إلى حالته ويراقبها في عمليته الخاصة. يكون TileService في وضع النشاط مخصّصًا لـ onTileAdded() وonTileRemoved() وأحداث النقر وعندما يطلبه تطبيق.

ننصحك باستخدام الوضع النشط إذا كان TileService يتلقّى إشعارًا عندما يجب أن يتم تعديل حالة المربّع من خلال عمليته الخاصة. تحدّ البلاطات النشطة من الضغط على النظام لأنّه ليس من الضروري ربطها في كل مرة تظهر فيها لوحة &quot;الإعدادات السريعة&quot; للمستخدم.

يمكن استدعاء الطريقة الثابتة TileService.requestListeningState() لطلب بدء حالة الاستماع وتلقّي ردّ على onStartListening().

يمكنك الإعلان عن الوضع النشط من خلال إضافة META_DATA_ACTIVE_TILE إلى ملف البيان الخاص بتطبيقك.

<service ...>
    <meta-data android:name="android.service.quicksettings.ACTIVE_TILE"
         android:value="true" />
    ...
</service>

الوضع غير النشط

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

يتلقّى تطبيقك ردّ اتصال إلى onStartListening() بعد أن يفتح المستخدم لوحة &quot;الإعدادات السريعة&quot;. يمكنك تعديل عنصر Tile عدة مرات كما تريد بين onStartListening() وonStopListening().

لست بحاجة إلى توضيح أنّ التطبيق لا يعمل في الوضع غير النشط، بل عليك فقط عدم إضافة META_DATA_ACTIVE_TILE إلى ملف بيان التطبيق.

نظرة عامة على حالات المربّعات

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

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

    على سبيل المثال، بالنسبة إلى مربّع تطبيق اللياقة البدنية الذي يتيح للمستخدمين بدء جلسة تمرين محدّدة المدة، يعني STATE_ACTIVE أنّ المستخدم قد بدأ جلسة تمرين وأنّ المؤقت يعمل.

  • STATE_INACTIVE: يشير إلى حالة إيقاف أو إيقاف مؤقت. ويمكن للمستخدم التفاعل مع تطبيقك المصغّر في هذه الحالة.

    لاستخدام مثال مربّع تطبيق اللياقة البدنية مرة أخرى، يعني المربّع في STATE_INACTIVE أنّ المستخدم لم يبدأ جلسة تمرين، ولكن يمكنه إجراء ذلك إذا أراد.

  • STATE_UNAVAILABLE: يشير إلى حالة غير متاحة مؤقتًا. لا يمكن للمستخدم التفاعل مع تطبيقك المصغّر عندما يكون في هذه الحالة.

    على سبيل المثال، يشير المربّع في STATE_UNAVAILABLE إلى أنّ المربّع غير متاح حاليًا للمستخدم لسبب ما.

يضبط النظام الحالة الأولية لعنصر Tile فقط. يمكنك ضبط حالة العنصر Tile طوال بقية دورة حياته.

قد يلوّن النظام رمز البلاطة وخلفيتها ليعكسا حالة العنصر Tile. تكون عناصر Tile التي تم ضبطها على STATE_ACTIVE هي الأغمق، وتصبح العناصر التي تم ضبطها على STATE_INACTIVE وSTATE_UNAVAILABLE أفتح بشكل متزايد. ويختلف اللون الدقيق حسب الشركة المصنّعة والإصدار.

تلوين مربّع شبكة VPN ليعكس حالات العنصر
الشكل 4. أمثلة على مربّع مظلّل ليعكس حالة المربّع (الحالات النشطة وغير النشطة وغير المتوفّرة، على التوالي)

تعديل المربّع

يمكنك تعديل البلاطة بعد تلقّي ردّ على طلبك على الرقم onStartListening(). استنادًا إلى وضع اللوحة، يمكن تعديلها مرة واحدة على الأقل إلى أن تتلقّى ردّ اتصال إلى onStopListening().

في وضع النشاط، يمكنك تعديل اللوحة مرة واحدة فقط قبل تلقّي إجراء ردّ الاتصال إلى onStopListening(). في الوضع غير النشط، يمكنك تعديل المربّع عدة مرات كما تريد بين onStartListening() وonStopListening().

يمكنك استرداد العنصر Tile من خلال طلب getQsTile(). لتعديل حقول معيّنة في عنصر Tile، استخدِم الطرق التالية:

يجب استدعاء updateTile() لتعديل المربّع بعد الانتهاء من ضبط حقول العنصر Tile على القيم الصحيحة. سيؤدي ذلك إلى أن يحلّل النظام بيانات المربّعات المعدَّلة ويعدّل واجهة المستخدم.

Kotlin

data class StateModel(val enabled: Boolean, val label: String, val icon: Icon)

override fun onStartListening() {
  super.onStartListening()
  val state = getStateFromService()
  qsTile.label = state.label
  qsTile.contentDescription = tile.label
  qsTile.state = if (state.enabled) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
  qsTile.icon = state.icon
  qsTile.updateTile()
}

Java

public class StateModel {
  final boolean enabled;
  final String label;
  final Icon icon;

  public StateModel(boolean e, String l, Icon i) {
    enabled = e;
    label = l;
    icon = i;
  }
}

@Override
public void onStartListening() {
  super.onStartListening();
  StateModel state = getStateFromService();
  Tile tile = getQsTile();
  tile.setLabel(state.label);
  tile.setContentDescription(state.label);
  tile.setState(state.enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
  tile.setIcon(state.icon);
  tile.updateTile();
}

التعامل مع النقرات

يمكن للمستخدمين النقر على المربّع لتنفيذ إجراء إذا كان المربّع في STATE_ACTIVE أو STATE_INACTIVE. بعد ذلك، يستدعي النظام دالة onClick() معاودة الاتصال في تطبيقك.

بعد أن يتلقّى تطبيقك ردّ اتصال إلى onClick()، يمكنه عرض مربّع حوار أو نشاط، أو بدء عمل في الخلفية، أو تغيير حالة البلاطة.

Kotlin

var clicks = 0
override fun onClick() {
  super.onClick()
  counter++
  qsTile.state = if (counter % 2 == 0) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
  qsTile.label = "Clicked $counter times"
  qsTile.contentDescription = qsTile.label
  qsTile.updateTile()
}

Java

int clicks = 0;

@Override
public void onClick() {
  super.onClick();
  counter++;
  Tile tile = getQsTile();
  tile.setState((counter % 2 == 0) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
  tile.setLabel("Clicked " + counter + " times");
  tile.setContentDescription(tile.getLabel());
  tile.updateTile();
}

فتح مربّع حوار

يؤدي النقر على showDialog() إلى تصغير لوحة "الإعدادات السريعة" وعرض مربّع حوار. استخدِم مربّع حوار لإضافة سياق إلى الإجراء إذا كان يتطلّب إدخال بيانات إضافية أو الحصول على موافقة المستخدم.

تشغيل نشاط

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

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

يؤدي النقر مع الاستمرار على مربّع إلى ظهور شاشة معلومات التطبيق للمستخدم. لإلغاء هذا السلوك وتشغيل نشاط لإعداد التفضيلات بدلاً من ذلك، أضِف <intent-filter> إلى أحد أنشطتك باستخدام ACTION_QS_TILE_PREFERENCES.

بدءًا من الإصدار 28 من واجهة برمجة التطبيقات Android، يجب أن يتضمّن PendingIntent ما يلي:Intent.FLAG_ACTIVITY_NEW_TASK

if (Build.VERSION.SDK_INT >= 28) {
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}

يمكنك بدلاً من ذلك إضافة العلامة في AndroidManifest.xml في القسم Activity المحدّد.

وضع علامة على المربّع باعتباره قابلاً للتبديل

ننصحك بوضع علامة على المربّع باعتباره قابلاً للتبديل إذا كان يعمل بشكل أساسي كمفتاح تبديل ثنائي الحالة (وهو السلوك الأكثر شيوعًا للمربّعات). يساعد ذلك في تقديم معلومات عن سلوك اللوحة إلى نظام التشغيل وتحسين إمكانية الوصول بشكل عام.

اضبط البيانات الوصفية TOGGLEABLE_TILE على true لوضع علامة على المربّع باعتباره قابلاً للتبديل.

<service ...>
  <meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE"
    android:value="true" />
</service>

تنفيذ الإجراءات الآمنة فقط على الأجهزة المقفلة بشكل آمن

قد تظهر اللوحة في أعلى شاشة القفل على الأجهزة المقفلة. إذا كانت اللوحة تتضمّن معلومات حساسة، تحقَّق من قيمة isSecure() لتحديد ما إذا كان الجهاز في حالة آمنة، ويجب أن يغيّر TileService سلوكه وفقًا لذلك.

إذا كان إجراء اللوحة آمنًا للتنفيذ أثناء قفل الشاشة، استخدِم startActivity() لتشغيل نشاط أعلى شاشة القفل.

إذا كان إجراء البلاطة غير آمن، استخدِم unlockAndRun() لطلب فتح قفل الجهاز من المستخدم. في حال النجاح، ينفّذ النظام العنصر Runnable الذي تمرّره إلى هذه الطريقة.

تصنيف المربّع

لتحسين تجربة المستخدم في "الإعدادات السريعة"، يمكنك تصنيف المربّع. ينظّم النظام المربّعات في فئات مثل &quot;الاتصال&quot; و&quot;الشاشة&quot; و&quot;الخصوصية&quot;. يستخدم النظام هذه الفئات لترتيب وتجميع المربّعات في وضع تعديل &quot;الإعدادات السريعة&quot;، ما يسهّل على المستخدمين العثور عليها وإدارتها.

التنفيذ

لتحديد فئة لـ TileService، أضِف حقل بيانات وصفية إلى بيان الخدمة في ملف AndroidManifest.xml:

  • في AndroidManifest.xml، ضمن العنصر <service> الخاص بـ TileService، أضِف العنصر <meta-data>.
  • android:name: اضبط هذه القيمة على android.service.quicksettings.TILE_CATEGORY.
  • استبدِل android:value بأحد ثوابت الفئات المحدّدة مسبقًا، مثل android.service.quicksettings.CATEGORY_CONNECTIVITY أو android.service.quicksettings.CATEGORY_DISPLAY.

كما هو موضّح في المثال التالي:

<service
    android:name=".MyConnectivityTileService"
    [...]
    >
    <meta-data android:name="android.service.quicksettings.TILE_CATEGORY"
        android:value="android.service.quicksettings.CATEGORY_CONNECTIVITY" />
</service>

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

في حال عدم تحديد فئة، يحدّد النظام تلقائيًا فئة تلقائية:

  • من تطبيقات النظام: للبلاطات التي تشكّل جزءًا من تطبيق نظام
  • من التطبيقات التي ثبّتها: لعرض مربّعات من التطبيقات التي ثبّتها.

على الرغم من أنّ أجهزة Google Pixel تستخدم الفئات في &quot;الإعدادات السريعة&quot;، يمكن لمصنّعي المعدات الأصلية استخدام معلومات الفئات هذه أو تجاهلها في واجهات المستخدم الخاصة بأنظمتهم.

مطالبة المستخدم بإضافة البلاطة

لإضافة المربّع يدويًا، على المستخدمين اتّباع عدة خطوات:

  1. مرِّر سريعًا للأسفل لفتح لوحة "الإعدادات السريعة".
  2. انقر على زر التعديل.
  3. انتقِل بين جميع المربّعات على جهاز الشخص الآخر إلى أن يعثر على مربّعك.
  4. اضغط مع الاستمرار على المربّع واسحبه إلى قائمة المربّعات النشطة.

يمكن للمستخدم أيضًا نقل مربّعك أو إزالته في أي وقت.

اعتبارًا من الإصدار 13 من نظام التشغيل Android، يمكنك استخدام طريقة requestAddTileService() لتسهيل إضافة المستخدمين لرموزك إلى الأجهزة. تطلب هذه الطريقة من المستخدمين إضافة تطبيقك سريعًا إلى لوحة &quot;الإعدادات السريعة&quot;. يتضمّن الطلب اسم التطبيق والتصنيف المقدَّم والرمز.

طلب واجهة برمجة التطبيقات Quick Settings Placement API
الشكل 5. طلب Quick Settings Placement API
public void requestAddTileService (
  ComponentName tileServiceComponentName,
  CharSequence tileLabel,
  Icon icon,
  Executor resultExecutor,
  Consumer<Integer> resultCallback
)

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

استخدِم تقديرك الخاص عند تحديد الوقت وعدد المرات التي تريد فيها مطالبة المستخدمين بإجراء تقييم. ننصحك باستدعاء requestAddTileService() فقط في السياق، مثلاً عندما يتفاعل المستخدم لأول مرة مع ميزة يسهّلها تطبيقك.

يمكن للنظام أن يختار التوقف عن معالجة الطلبات الخاصة بـ ComponentName معيّن إذا تم رفضها من قِبل المستخدم عدة مرات من قبل. يتم تحديد المستخدم من خلال Context المستخدَم لاسترداد هذه الخدمة، ويجب أن يتطابق مع المستخدم الحالي.