Tạo ô Cài đặt nhanh tuỳ chỉnh cho ứng dụng

Cài đặt nhanh là các thẻ thông tin hiển thị trong bảng điều khiển Cài đặt nhanh, đại diện cho các hành động mà người dùng có thể nhấn để nhanh chóng hoàn tất các tác vụ lặp lại. Ứng dụng của bạn có thể cung cấp một ô tuỳ chỉnh cho người dùng thông qua lớp TileService và sử dụng đối tượng Tile để theo dõi trạng thái của ô. Ví dụ: bạn có thể tạo một ô cho phép người dùng bật hoặc tắt VPN do ứng dụng của bạn cung cấp.

Bảng Cài đặt nhanh có ô VPN ở trạng thái bật và tắt
Hình 1. Bảng điều khiển Cài đặt nhanh có ô VPN bật và tắt.

Quyết định thời điểm tạo ô

Bạn nên tạo thẻ thông tin cho các chức năng cụ thể mà bạn muốn người dùng thường xuyên truy cập hoặc cần truy cập nhanh (hoặc cả hai). Các thẻ thông tin hiệu quả nhất là những thẻ thông tin đáp ứng cả hai tiêu chí này, giúp truy cập nhanh vào các hành động thường xuyên được thực hiện.

Ví dụ: bạn có thể tạo một ô cho ứng dụng theo dõi thể chất cho phép người dùng nhanh chóng bắt đầu một phiên tập luyện. Tuy nhiên, bạn không nên tạo một ô cho cùng một ứng dụng cho phép người dùng xem lại toàn bộ nhật ký bài tập.

Các trường hợp sử dụng ô ứng dụng theo dõi thể chất
Hình 2. Ví dụ về các thẻ thông tin được đề xuất so với các thẻ thông tin không được đề xuất cho một ứng dụng theo dõi thể chất.

Để giúp cải thiện khả năng tìm thấy và tính dễ sử dụng của thẻ thông tin, bạn nên tránh một số phương pháp sau:

  • Tránh sử dụng thẻ thông tin để chạy ứng dụng. Thay vào đó, hãy sử dụng lối tắt ứng dụng hoặc trình chạy tiêu chuẩn.

  • Tránh sử dụng thẻ thông tin cho các hành động một lần của người dùng. Thay vào đó, hãy sử dụng lối tắt ứng dụng hoặc a thông báo.

  • Tránh tạo quá nhiều thẻ thông tin. Bạn nên tạo tối đa 2 thẻ thông tin cho mỗi ứng dụng. Thay vào đó, hãy sử dụng lối tắt ứng dụng.

  • Tránh sử dụng thẻ thông tin hiển thị thông tin nhưng không tương tác với người dùng. Thay vào đó, hãy sử dụng thông báo hoặc tiện ích.

Tạo ô

Để tạo ô, trước tiên, bạn cần tạo một biểu tượng ô thích hợp, sau đó tạo và khai báo TileService trong tệp kê khai của ứng dụng.

Mẫu Cài đặt nhanh cung cấp một ví dụ về cách tạo và quản lý ô.

Tạo biểu tượng tuỳ chỉnh

Bạn cần cung cấp một Biểu tượng tùy chỉnh hiển thị trên ô trong bảng điều khiển Cài đặt nhanh. (Bạn sẽ thêm biểu tượng này khi khai báo TileService, được mô tả trong phần tiếp theo.) Biểu tượng phải có màu trắng đồng nhất với nền trong suốt, kích thước 24 x 24dp và ở dạng VectorDrawable.

Ví dụ về một vectơ vẽ được
Hình 3. Ví dụ về một vectơ vẽ được.

Tạo một biểu tượng gợi ý trực quan về mục đích của ô. Điều này giúp người dùng dễ dàng xác định xem thẻ thông tin có phù hợp với nhu cầu của họ hay không. Ví dụ: bạn có thể tạo một biểu tượng đồng hồ bấm giờ cho ô của ứng dụng theo dõi thể chất cho phép người dùng bắt đầu một phiên tập luyện.

Tạo và khai báo TileService

Tạo một dịch vụ cho thẻ thông tin của bạn mở rộng lớp 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();
  }
}

Khai báo TileService trong tệp kê khai của ứng dụng. Thêm tên và nhãn của TileService, biểu tượng tuỳ chỉnh mà bạn đã tạo trong phần trước và quyền thích hợp.

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

Quản lý TileService

Sau khi tạo và khai báo TileService trong tệp kê khai ứng dụng, bạn phải quản lý trạng thái của dịch vụ đó.

TileService là một dịch vụ ràng buộc. TileService của bạn được ràng buộc khi ứng dụng yêu cầu hoặc nếu hệ thống cần giao tiếp với dịch vụ đó. Vòng đời dịch vụ ràng buộc điển hình chứa 4 phương thức gọi lại sau: onCreate(), onBind(), onUnbind()onDestroy(). Các phương thức này được hệ thống gọi mỗi khi dịch vụ chuyển sang một giai đoạn vòng đời mới.

Tổng quan về vòng đời TileService

Ngoài các lệnh gọi lại kiểm soát vòng đời dịch vụ ràng buộc, bạn phải triển khai các phương thức khác dành riêng cho vòng đời TileService. Các phương thức này có thể được gọi bên ngoài onCreate()onDestroy() vì các phương thức vòng đời Service và các phương thức vòng đời TileService được gọi trong 2 luồng không đồng bộ riêng biệt.

Vòng đời TileService chứa các phương thức sau, được hệ thống gọi mỗi khi TileService của bạn chuyển sang một giai đoạn vòng đời mới:

  • onTileAdded(): Phương thức này chỉ được gọi khi người dùng thêm thẻ thông tin của bạn lần đầu tiên và nếu người dùng xoá rồi thêm lại thẻ thông tin của bạn. Đây là thời điểm tốt nhất để thực hiện bất kỳ thao tác khởi chạy một lần nào. Tuy nhiên, thao tác này có thể không đáp ứng được tất cả các thao tác khởi chạy cần thiết.

  • onStartListening()onStopListening(): Các phương thức này được gọi mỗi khi ứng dụng của bạn cập nhật thẻ thông tin và thường xuyên được gọi. TileService vẫn được ràng buộc giữa onStartListening()onStopListening(), cho phép ứng dụng của bạn sửa đổi ô thông tin và đẩy các bản cập nhật.

  • onTileRemoved(): Phương thức này chỉ được gọi nếu người dùng xoá ô thông tin của bạn.

Chọn chế độ nghe

TileService của bạn nghe ở chế độ đang hoạt động hoặc chế độ không hoạt động. Bạn nên sử dụng chế độ đang hoạt động mà bạn cần khai báo trong tệp kê khai ứng dụng. Nếu không, TileService là chế độ chuẩn và không cần được khai báo.

Đừng cho rằng TileService của bạn sẽ tồn tại bên ngoài cặp phương thức onStartListening()onStopListening().

Sử dụng chế độ đang hoạt động cho TileService nghe và giám sát trạng thái của chính nó trong quy trình riêng. TileService ở chế độ đang hoạt động được ràng buộc cho onTileAdded(), onTileRemoved(), các sự kiện nhấn và khi được quy trình ứng dụng yêu cầu.

Bạn nên sử dụng chế độ đang hoạt động nếu TileService của bạn được thông báo khi trạng thái ô phải được cập nhật theo quy trình riêng. Các thẻ thông tin đang hoạt động hạn chế áp lực lên hệ thống vì chúng không phải được ràng buộc mỗi khi bảng điều khiển Cài đặt nhanh hiển thị cho người dùng.

Bạn có thể gọi phương thức TileService.requestListeningState() tĩnh để yêu cầu bắt đầu trạng thái nghe và nhận lệnh gọi lại đến onStartListening().

Bạn có thể khai báo chế độ đang hoạt động bằng cách thêm META_DATA_ACTIVE_TILE vào tệp kê khai của ứng dụng.

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

Chế độ không hoạt động

Chế độ không hoạt động là chế độ tiêu chuẩn. TileService ở chế độ không hoạt động nếu được ràng buộc mỗi khi thẻ thông tin của bạn hiển thị cho người dùng. Điều này có nghĩa là TileService của bạn có thể được tạo và ràng buộc lại vào những thời điểm ngoài tầm kiểm soát. Dịch vụ này cũng có thể bị huỷ ràng buộc và bị huỷ khi người dùng không xem ô.

Ứng dụng của bạn nhận được lệnh gọi lại đến onStartListening() sau khi người dùng mở bảng điều khiển Cài đặt nhanh. Bạn có thể cập nhật đối tượng Tile bao nhiêu lần tuỳ ý giữa onStartListening()onStopListening().

Bạn không cần khai báo chế độ không hoạt động – chỉ cần không thêm META_DATA_ACTIVE_TILE vào tệp kê khai của ứng dụng.

Tổng quan về trạng thái thẻ thông tin

Sau khi người dùng thêm thẻ thông tin của bạn, thẻ thông tin đó luôn tồn tại ở một trong các trạng thái sau.

  • STATE_ACTIVE: Cho biết trạng thái bật hoặc đã bật. Người dùng có thể tương tác với thẻ thông tin của bạn khi ở trạng thái này.

    Ví dụ: đối với ô ứng dụng theo dõi thể chất cho phép người dùng bắt đầu một phiên tập luyện có tính thời gian, STATE_ACTIVE có nghĩa là người dùng đã bắt đầu một phiên tập luyện và bộ hẹn giờ đang chạy.

  • STATE_INACTIVE: Cho biết trạng thái tắt hoặc tạm dừng. Người dùng có thể tương tác với thẻ thông tin của bạn khi ở trạng thái này.

    Để sử dụng lại ví dụ về ô ứng dụng theo dõi thể chất, ô ở STATE_INACTIVE có nghĩa là người dùng chưa bắt đầu một phiên tập luyện, nhưng có thể thực hiện nếu muốn.

  • STATE_UNAVAILABLE: Cho biết trạng thái tạm thời không dùng được. Người dùng không thể tương tác với thẻ thông tin của bạn khi ở trạng thái này.

    Ví dụ: ô ở STATE_UNAVAILABLE có nghĩa là ô hiện không dùng được cho người dùng vì một lý do nào đó.

Hệ thống chỉ đặt trạng thái ban đầu của đối tượng Tile. Bạn đặt trạng thái của đối tượng Tile trong suốt phần còn lại của vòng đời.

Hệ thống có thể tô màu biểu tượng và nền của thẻ thông tin để phản ánh trạng thái của đối tượng Tile. Các đối tượng Tile được đặt thành STATE_ACTIVE là tối nhất, với STATE_INACTIVESTATE_UNAVAILABLE ngày càng sáng hơn. Màu sắc chính xác tuỳ thuộc vào nhà sản xuất và phiên bản.

Ô VPN được tô màu để phản ánh trạng thái của đối tượng
Hình 4. Ví dụ về một ô được tô màu để phản ánh trạng thái ô (trạng thái đang hoạt động, không hoạt động và không dùng được).

Cập nhật ô

Bạn có thể cập nhật ô sau khi nhận được cuộc gọi lại đến onStartListening(). Tuỳ thuộc vào chế độ của ô, ô của bạn có thể được cập nhật ít nhất một lần cho đến khi nhận được cuộc gọi lại đến onStopListening().

Ở chế độ đang hoạt động, bạn có thể cập nhật ô chính xác một lần trước khi nhận được lệnh gọi lại đến onStopListening(). Ở chế độ không hoạt động, bạn có thể cập nhật ô bao nhiêu lần tuỳ ý giữa onStartListening()onStopListening().

Bạn có thể truy xuất đối tượng Tile bằng cách gọi getQsTile(). Để cập nhật các trường cụ thể của đối tượng Tile, hãy gọi các phương thức sau:

Bạn phải gọi updateTile() để cập nhật ô sau khi đặt các trường của đối tượng Tile thành giá trị chính xác. Thao tác này sẽ khiến hệ thống phân tích cú pháp dữ liệu ô đã cập nhật và cập nhật giao diện người dùng.

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();
}

Xử lý thao tác nhấn

Người dùng có thể nhấn vào ô của bạn để kích hoạt một hành động nếu ô của bạn ở STATE_ACTIVE hoặc STATE_INACTIVE. Sau đó, hệ thống sẽ gọi lệnh gọi lại của ứng dụng. onClick()

Sau khi ứng dụng của bạn nhận được lệnh gọi lại đến onClick(), ứng dụng có thể chạy một hộp thoại hoặc hoạt động, kích hoạt công việc ở chế độ nền hoặc thay đổi trạng thái của thẻ thông tin.

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();
}

Chạy hộp thoại

showDialog() thu gọn bảng điều khiển Cài đặt nhanh và hiển thị một hộp thoại. Sử dụng hộp thoại để thêm ngữ cảnh vào hành động của bạn nếu hành động đó yêu cầu thêm thông tin đầu vào hoặc sự đồng ý của người dùng.

Chạy một hoạt động

startActivityAndCollapse() bắt đầu một hoạt động trong khi thu gọn bảng điều khiển. Các hoạt động rất hữu ích nếu có thông tin chi tiết hơn để hiển thị so với trong hộp thoại hoặc nếu hành động của bạn có tính tương tác cao.

Nếu ứng dụng của bạn yêu cầu lượt tương tác của người dùng đáng kể, thì ứng dụng chỉ nên chạy một hoạt động như là biện pháp cuối cùng. Thay vào đó, hãy cân nhắc sử dụng hộp thoại hoặc nút bật/tắt.

Việc nhấn và giữ một ô sẽ nhắc người dùng màn hình Thông tin ứng dụng. Để ghi đè hành vi này và thay vào đó chạy một hoạt động để thiết lập lựa chọn ưu tiên, hãy thêm một <intent-filter> vào một trong các hoạt động của bạn bằng ACTION_QS_TILE_PREFERENCES.

Bắt đầu từ Android API 28, PendingIntent phải có Intent.FLAG_ACTIVITY_NEW_TASK:

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

Ngoài ra, bạn có thể thêm cờ trong AndroidManifest.xml trong phần Activity cụ thể.

Đánh dấu ô là có thể bật/tắt

Bạn nên đánh dấu ô là có thể bật/tắt nếu ô này hoạt động chủ yếu như một nút chuyển hai trạng thái (đây là hành vi phổ biến nhất của ô). Điều này giúp cung cấp thông tin về hành vi của ô cho hệ điều hành và cải thiện khả năng hỗ trợ tiếp cận tổng thể.

Đặt siêu dữ liệu TOGGLEABLE_TILE thành true để đánh dấu ô là có thể bật/tắt.

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

Chỉ thực hiện các hành động an toàn trên các thiết bị được khoá an toàn

Thẻ thông tin của bạn có thể hiển thị trên cùng màn hình khoá trên các thiết bị bị khoá. Nếu ô chứa thông tin nhạy cảm, hãy kiểm tra giá trị của isSecure() để xác định xem thiết bị có ở trạng thái an toàn hay không và TileService của bạn sẽ thay đổi hành vi cho phù hợp.

Nếu hành động của ô an toàn để thực hiện khi bị khoá, hãy sử dụng startActivity() để chạy một hoạt động trên cùng màn hình khoá.

Nếu hành động của ô không an toàn, hãy sử dụng unlockAndRun() để nhắc người dùng mở khoá thiết bị. Nếu thành công, hệ thống sẽ thực thi đối tượng Runnable mà bạn truyền vào phương thức này.

Phân loại thẻ thông tin

Để nâng cao trải nghiệm người dùng trong Cài đặt nhanh, bạn có thể phân loại ô. Hệ thống sắp xếp các thẻ thông tin thành các danh mục như Kết nối, Màn hình và Quyền riêng tư. Hệ thống sử dụng các danh mục này để sắp xếp và nhóm các thẻ thông tin ở chế độ chỉnh sửa Cài đặt nhanh, giúp người dùng dễ dàng tìm và quản lý.

Triển khai

Để chỉ định một danh mục cho TileService, hãy thêm một trường siêu dữ liệu vào phần khai báo dịch vụ trong tệp AndroidManifest.xml:

  • Trong AndroidManifest.xml, trong phần tử <service> cho TileService, hãy thêm phần tử <meta-data>.
  • android:name: Đặt giá trị này thành android.service.quicksettings.TILE_CATEGORY.
  • android:value: Chỉ định một trong các hằng số danh mục được xác định trước, chẳng hạn như android.service.quicksettings.CATEGORY_CONNECTIVITY hoặc android.service.quicksettings.CATEGORY_DISPLAY.

Như trong ví dụ sau:

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

API cung cấp một tập hợp các danh mục được xác định trước để bạn lựa chọn. Các danh mục này được xác định là các hằng số chuỗi trong lớp TileService.

Nếu không chỉ định danh mục, hệ thống sẽ tự động chỉ định một danh mục mặc định:

  • Từ các ứng dụng hệ thống: Đối với các thẻ thông tin là một phần của ứng dụng hệ thống.
  • Từ các ứng dụng bạn đã cài đặt: Đối với các thẻ thông tin từ các ứng dụng do người dùng cài đặt.

Mặc dù thiết bị Google Pixel sử dụng các danh mục trong Cài đặt nhanh, nhưng OEM có thể sử dụng hoặc bỏ qua thông tin danh mục này trong giao diện người dùng hệ thống tương ứng.

Nhắc người dùng thêm ô thông tin

Để thêm ô theo cách thủ công, người dùng phải làm theo một số bước:

  1. Vuốt xuống để mở bảng điều khiển Cài đặt nhanh.
  2. Nhấn vào nút chỉnh sửa.
  3. Cuộn qua tất cả các ô trên thiết bị cho đến khi tìm thấy ô của bạn.
  4. Nhấn và giữ ô của bạn, sau đó kéo ô đó vào danh sách các ô đang hoạt động.

Người dùng cũng có thể di chuyển hoặc xoá thẻ thông tin của bạn bất cứ lúc nào.

Bắt đầu từ Android 13, bạn có thể sử dụng phương thức requestAddTileService() để giúp người dùng dễ dàng thêm ô của bạn vào thiết bị hơn. Phương thức này nhắc người dùng bằng một yêu cầu nhanh chóng thêm ô của bạn trực tiếp vào bảng điều khiển Cài đặt nhanh. Lời nhắc bao gồm tên ứng dụng, nhãn và biểu tượng được cung cấp.

Lời nhắc về API Vị trí của chế độ Cài đặt nhanh
Hình 5. Lời nhắc API Vị trí Cài đặt nhanh.
public void requestAddTileService (
  ComponentName tileServiceComponentName,
  CharSequence tileLabel,
  Icon icon,
  Executor resultExecutor,
  Consumer<Integer> resultCallback
)

Lệnh gọi lại chứa thông tin về việc ô có được thêm hay không, nếu ô đã có ở đó hay nếu có lỗi xảy ra.

Hãy sử dụng quyền quyết định của bạn khi quyết định thời điểm và tần suất nhắc người dùng. Bạn chỉ nên gọi requestAddTileService() trong ngữ cảnh – chẳng hạn như khi người dùng tương tác lần đầu với một tính năng mà ô thông tin của bạn hỗ trợ.

Hệ thống có thể chọn ngừng xử lý các yêu cầu cho một ComponentName nhất định nếu người dùng đã từ chối đủ số lần trước đó. Người dùng được xác định từ Context dùng để truy xuất dịch vụ này – phải khớp với người dùng hiện tại.