Tạo một tiện ích bằng tính năng Xem nhanh

1. Trước khi bắt đầu

Trong lớp học lập trình này, bạn sẽ được hướng dẫn từng bước về quy trình tạo tiện ích ứng dụng cho SociaLite. Trước tiên, bạn sẽ tạo một tiện ích Glance đơn giản rồi thêm tiện ích này vào SociaLite và màn hình chính. Tiếp theo, bạn sẽ thêm một trạng thái ban đầu vào tiện ích này bằng các thành phần Glance và giao diện Glance. Sau đó, trong lớp học lập trình này, bạn sẽ được hướng dẫn từng bước về cách hỗ trợ hoạt động tương tác của người dùng để chọn người liên hệ yêu thích từ tiện ích của bạn. Cuối cùng, bạn sẽ học cách cập nhật tiện ích trong ứng dụng của mình.

Điều kiện tiên quyết

  • Có kiến thức cơ bản về Kotlin.
  • Hoàn thành lớp học lập trình Thiết lập Android Studio hoặc thành thạo cách dùng Android Studio cũng như cách kiểm thử ứng dụng trong trình mô phỏng Android 15 hoặc trên một thiết bị thực chạy Android 15.
  • Có kiến thức cơ bản về Hilt.
  • Có kiến thức cơ bản về Compose. Glance không dùng thành phần kết hợp có trong Jetpack Compose, nhưng sử dụng lại kiểu khung và kiểu lập trình.

Kiến thức bạn sẽ học được trong lớp học lập trình này

  • Cách định cấu hình ứng dụng của bạn để hỗ trợ các tiện ích.
  • Cách sử dụng thành phần Glance để tạo bố cục thích ứng.
  • Cách sử dụng GlanceTheme để hỗ trợ màu động trên màn hình chính của người dùng.
  • Cách xử lý hoạt động tương tác của người dùng trong tiện ích.
  • Cách cập nhật tiện ích của bạn trong ứng dụng.

Bạn cần có

  • Android Studio phiên bản mới nhất.
  • Một thiết bị thử nghiệm hoặc trình mô phỏng chạy Android 12 trở lên.
  • SDK Android 12 trở lên.

Màn hình chính của Android có tiện ích SociaLite

2. Bắt đầu thiết lập

Lấy mã khởi đầu

  1. Nếu bạn đã hoàn thành lớp học lập trình Xử lý biện pháp thực thi chế độ tràn viền trên Android 15 hoặc lớp học lập trình Thêm ảnh động xem trước thao tác quay lại, hãy tiếp tục với phần Thêm tiện ích vì bạn đã có mã khởi đầu.
  2. Tải mã khởi đầu xuống từ GitHub.

Ngoài ra, bạn có thể sao chép kho lưu trữ và xem nhánh codelab_improve_android_experience_2024.

 git clone git@github.com:android/socialite.git
 cd socialite
 git checkout codelab_improve_android_experience_2024
  1. Mở ứng dụng SociaLite trong Android Studio rồi chạy ứng dụng này trên trình mô phỏng hoặc thiết bị chạy Android 15. Bạn thấy một màn hình giống như sau:

fb043d54dd01b3e5.png

Ứng dụng SociaLite có chế độ thao tác bằng cử chỉ

3. Thêm tiện ích

Tiện ích là gì?

Tiện ích là một phần trong ứng dụng của bạn và có thể được nhúng bên trong các ứng dụng Android khác, phổ biến nhất là trên màn hình chính của người dùng.

Khi bạn thêm các tiện ích vào ứng dụng của mình, người dùng có thể nhanh chóng bắt đầu các thao tác thông thường, xem thông tin có thể xem nhanh và tuỳ chỉnh thiết bị bằng nội dung của bạn.

Glance là gì?

Jetpack Glance là một thư viện để viết các tiện ích bằng một API giống như Compose trong Kotlin. Glance có một số ưu điểm giống như của Compose, chẳng hạn như thành phần kết hợp lại, mã giao diện người dùng có thể khai báo được viết trong Kotlin và các thành phần được định sẵn. Khi dùng Glance, bạn không còn cần sử dụng khung hiển thị từ xa XML trong tiện ích.

Tạo tiện ích

Các tiện ích trên Android được khai báo trong AndroidManifest dưới dạng một phần tử <receiver>. Phần tử nhận này có thể được xuất và có khả năng xử lý ý định hành động android.appwidget.action.APPWIDGET_UPDATE, cũng như cung cấp một tệp cài đặt tiện ích ứng dụng thông qua một phần tử siêu dữ liệu có tên là android.appwidget.provider.

Thêm tiện ích vào SociaLite

Màn hình chính của Android có tiện ích SociaLite

Bạn muốn thêm một tiện ích vào SociaLite để người dùng có thể xem người liên hệ họ yêu thích và liệu họ có tin nhắn chưa đọc từ người đó hay không. Nếu có, họ có thể nhấn vào tiện ích và tiện ích sẽ đưa họ đến cuộc trò chuyện với người liên hệ yêu thích. Hoặc, bạn có thể dùng các thành phần Glance và tuỳ chỉnh giao diện để đảm bảo tiện ích của bạn có giao diện đẹp nhất bằng cách sử dụng thiết kế đáp ứng và màu động.

Để bắt đầu, bạn có thể thêm một tiện ích tĩnh "Hello World" (Xin chào) vào SociaLite. Sau đó, mở rộng các chức năng của thiết ích.

Để thực hiện việc này, hãy làm như sau:

  1. Thêm các phần phụ thuộc Glance vào ứng dụng của bạn.
  2. Tạo phương thức triển khai GlanceAppWidget.
  3. Tạo GlanceAppWidgetReceiver.
  4. Định cấu hình tiện ích của bạn bằng một tệp XML chứa thông tin về tiện ích ứng dụng.
  5. Thêm phần tử nhận và thông tin về tiện ích ứng dụng vào tệp AndroidManifest.xml.

Thêm Glance vào dự án

Mã khởi đầu này đã thêm các phiên bản Glance và toạ độ thư viện vào danh mục phiên bản của SociaLite: libs.versions.toml.

libs.versions.toml

[versions]
//..

glance = "1.1.1"

[libraries]
glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "glance" }
glance-material = { group = "androidx.glance", name = "glance-material3", version.ref = "glance" }

Hoặc, các phần phụ thuộc Glance được thêm vào tệp app/build.gradle.kts của SociaLite.

build.gradle.kts
dependencies {
...
implementation(libs.glance.appwidget)
implementation(libs.glance.material)

...
}
  • Nếu bạn đã sửa đổi những tệp này, hãy đồng bộ hoá dự án để tải các thư viện Glance xuống.

Tạo GlanceAppWidgetGlanceAppWidgetReceiver

Android sử dụng một bộ nhận tín hiệu truyền tin để thông báo cho SociaLite rằng một tiện ích đã được thêm, cần được cập nhật hoặc đã bị xoá. Glance cung cấp một lớp bộ nhận trừu tượng – GlanceAppWidgetReceiver sẽ mở rộng AppWidgetProvider.

Các phương thức triển khai GlanceAppWidgetReceiver cũng chịu trách nhiệm cung cấp các thực thể của GlanceAppWidget. Lớp này kết xuất các thành phần kết hợp Glance thành khung hiển thị từ xa.

Mã khởi đầu bao gồm 2 lớp: SocialiteAppWidget, mở rộng GlanceAppWidgetSocialiteAppWidgetReceiver, mở rộng GlanceAppWidgetReceiver.

Để bắt đầu, hãy làm theo các bước sau:

  1. Chuyển đến gói widget trong app/src/main/java/com/google/android/samples/socialite/.
  2. Mở lớp SociaLiteAppWidget. Lớp này ghi đè phương thức provideGlance.
  3. Thay thế TODO bằng một lệnh gọi đến provideContent rồi truyền hàm có khả năng kết hợp của tiện ích dưới dạng một tham số. Lúc này, tiện ích chỉ hiển thị Hello World của thông báo nhưng bạn có thể thêm chức năng khác sau trong lớp học lập trình này.
package com.google.android.samples.socialite.widget

import android.content.Context
import androidx.glance.GlanceId
import androidx.glance.GlanceTheme
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.provideContent
import androidx.glance.text.Text

class SociaLiteAppWidget : GlanceAppWidget() {
   override suspend fun provideGlance(context: Context, id: GlanceId) {
       provideContent {
           GlanceTheme {
               Text("Hello World")
           }
       }
   }
}
  1. Mở lớp SociaLiteAppWidgetReceiver trong gói widget. Lúc này, bộ nhận cung cấp một thực thể của SociaLiteWidget, nhưng bạn có thể thêm chức năng khác trong phần tiếp theo.
  2. Thay thế TODO bằng hàm khởi tạo SociaLiteAppWidget():
package com.google.android.samples.socialite.widget

import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver

class SociaLiteAppWidgetReceiver : GlanceAppWidgetReceiver() {
   override val glanceAppWidget: GlanceAppWidget = SociaLiteAppWidget()
}

Bạn hiện đã sẵn sàng định cấu hình Android để hiển thị tiện ích của mình và cho phép người dùng thêm tiện ích vào màn hình chính của họ.

Thêm thông tin của nhà cung cấp ứng dụng – tiện ích

  1. Nhấp chuột phải vào **res/xml** > New > XML resource file (Mới > Tệp tài nguyên XML).
  2. Nhập socialite_widget_info làm tên tệp và appwidget-provider làm phần tử gốc rồi nhấp vào OK. Tệp này bao gồm siêu dữ liệu cho appwidget được AppWidgetHost dùng để hiển thị tiện ích từ đầu.
  3. Thêm mã sau vào tệp socialite_widget_info.xml:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
   android:resizeMode="horizontal|vertical"
   android:updatePeriodMillis="3600000"
   android:minHeight="128dp"
   android:minWidth="128dp"

   android:minResizeHeight="128dp"
   android:minResizeWidth="128dp"
   android:configure="com.google.android.samples.socialite.widget.SociaLiteAppWidgetConfigActivity"
   android:widgetFeatures="configuration_optional|reconfigurable"
   android:previewImage="@drawable/widget_preview"
   android:maxResizeHeight="512dp"
   android:maxResizeWidth="512dp"
   android:targetCellWidth="2"
   android:targetCellHeight="2"
   android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>

Bảng sau đây đưa ra thông tin tổng quan về các thuộc tính trong đoạn mã này và mô tả từng thuộc tính:

Tên thuộc tính

Nội dung mô tả

resizeMode

Tiện ích của bạn có thể được đổi kích thước theo chiều dọc và chiều ngang.

targetCellWidth, targetCellHeight

Chỉ định kích thước mặc định của tiện ích khi được thêm vào màn hình chính.

updatePeriodMillis

Các chế độ điều khiển khi máy chủ tiện ích có thể quyết định làm mới tiện ích. Ứng dụng của bạn có thể cập nhật tiện ích bất cứ khi nào ứng dụng đang chạy và có thông tin mới cần hiển thị.

minResizeHeight,minResizeWidth

Thiết lập kích thước thu nhỏ tiện ích.

minHeight,minWidth

Chỉ định kích thước mặc định tối thiểu của tiện ích khi được thêm vào màn hình chính.

initialLayout

Cung cấp một bố cục ban đầu hiển thị khi Glance kết xuất các thành phần kết hợp.

previewImage

Cung cấp một hình ảnh tĩnh về tiện ích để hiển thị trong bộ chọn tiện ích.

widgetFeatures

Cho thấy nhiều tính năng mà tiện ích hỗ trợ. Đây đều là các gợi ý cho máy chủ tiện ích mà không thực sự làm thay đổi hành vi của tiện ích.

Trong lớp học lập trình này, các cờ cho máy chủ biết rằng tiện ích không yêu cầu cấu hình trước khi được thêm vào màn hình chính và có thể được định cấu hình sau khi thêm.

configure

Tên lớp hoạt động của cấu hình. Hoạt động này sẽ định cấu hình tiện ích vào lúc khác.

Để xem mọi thuộc tính có sẵn, bao gồm cả những tính năng trong API 31 trở lên, hãy xem AppWidgetProviderInfo.

Cập nhật AndroidManifest và kiểm thử

Cuối cùng, bạn đã sẵn sàng cập nhật tệp AndroidManifest.xml và có thể kiểm thử tiện ích của mình. Bạn định nghĩa phần tử receiver là một phần tử con của phần tử application trong tệp. Phần tử nhận này xử lý ý định APPWIDGET_UPDATE và cung cấp siêu dữ liệu appwidget cho Trình chạy Android.

Để bắt đầu, hãy làm theo các bước sau:

  1. Tạo một phần tử receiver để xuất SociaLiteAppWidgetReceiver. Sao chép và dán phần sau đây vào tệp AndroidManifest.xml sau phần tử application:
<receiver
   android:name=".widget.SociaLiteAppWidgetReceiver"
   android:exported="true"
   android:label="Favorite Contact">

   <intent-filter>
       <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
   </intent-filter>

   <meta-data
       android:name="android.appwidget.provider"
       android:resource="@xml/socialite_widget_info" />
</receiver>
  1. Biên dịch và chạy ứng dụng của bạn.
  2. Sau khi chạy ứng dụng, hãy thêm tiện ích vào màn hình chính. Ví dụ, trên thiết bị Pixel, hãy nhấn giữ nền rồi chọn Tiện ích > SociaLite. Bạn sẽ thêm được tiện ích của mình vào màn hình chính.

Màn hình chính của Android có tiện ích đang trong quá trình hoàn thiện. Tiện ích này trong suốt và hiển thị thông báo

Tiện ích có nội dung "Hello World" (Xin chào) và nền trong suốt. Trên thực tế, đây chưa phải là tiện ích có giao diện đẹp nhất hay tiện ích hoạt động hiệu quả nhất. Trong phần tiếp theo, bạn sẽ thêm một bố cục phức tạp hơn và trang trí cho tiện ích bằng một dải màu trong Material Design.

4. Cải thiện thiết kế

Hiện tại, tiện ích tĩnh của bạn cần có thêm nhiều tính năng nữa để trở thành một tiện ích hữu ích. Tiện ích hữu ích có những đặc điểm sau đây:

  • Đảm bảo nội dung luôn mới mẻ và ngắn gọn còn chức năng thì đơn giản.
  • Giảm thiểu các khoảng trống không mong muốn nhờ một bố cục có khả năng đổi kích thước.
  • Sử dụng màu lấy từ nền máy chủ tiện ích ứng dụng.

Để tìm hiểu sâu hơn về các yếu tố tạo nên một tiện ích hữu ích, hãy xem bài viết Tiện ích.

Thêm scaffold (bộ khung)

Giờ đây, bạn có thể cập nhật tiện ích của mình để hiển thị trong thành phần Scaffold của Glance.

Scaffold do thư viện Glance cung cấp. Đây là một API vị trí đơn giản để hiển thị giao diện người dùng tiện ích với một TitleBar. API này đặt màu nền thành GlanceTheme.colors.widgetBackground và áp dụng khoảng đệm. API này sẽ là thành phần cấp cao nhất của bạn.

Để bắt đầu, hãy làm theo các bước sau:

  1. Thay thế phương thức triển khai SociaLiteAppWidget bằng đoạn mã sau đây:
package com.google.android.samples.socialite.widget

import android.content.Context
import androidx.compose.runtime.Composable
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.ImageProvider
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.components.Scaffold
import androidx.glance.appwidget.components.TitleBar
import androidx.glance.appwidget.provideContent
import androidx.glance.layout.fillMaxSize
import androidx.glance.text.Text
import com.google.android.samples.socialite.R

class SociaLiteAppWidget : GlanceAppWidget() {
   override suspend fun provideGlance(context: Context, id: GlanceId) {
       provideContent {
           GlanceTheme() {
               Content()
           }
       }
   }

   @Composable
   private fun Content() {
       Scaffold(titleBar = {TitleBar(startIcon = ImageProvider(R.drawable.ic_launcher_monochrome), title = "SociaLite")},
           modifier = GlanceModifier.fillMaxSize()) {
           Text("Hello World")
       }
   }
}
  1. Để xem các bản cập nhật, hãy chạy lại ứng dụng rồi thêm một bản sao mới của tiện ích vào màn hình chính của bạn.

Màn hình chính của Android có tiện ích đang trong quá trình hoàn thiện. Tiện ích có tiêu đề và nền mờ

Lưu ý rằng, tiện ích là khung hiển thị từ xa được máy chủ bên ngoài hiển thị. Sau đó, bạn có thể thêm tính năng tự động cập nhật tiện ích trong ứng dụng. Từ giờ cho đến lúc đó, bạn phải thêm tiện ích từ bộ chọn tiện ích để xem các thay đổi về đoạn mã.

Bạn thấy đấy, như vậy đã tốt hơn nhiều rồi, nhưng khi so sánh tiện ích của bạn với những tiện ích khác, màu sắc có vẻ chưa hài hoà. Trên màn hình chính, thông thường các tiện ích sẽ điều chỉnh màu sắc theo chế độ cài đặt giao diện của người dùng. Đối với các mã thông báo có chứa màu động, giao diện của tiện ích có thể được điều chỉnh theo giao diện và hình nền trên thiết bị của bạn.

Thêm màu động

Giờ đây, bạn sẽ thêm mã thông báo có chứa màu widgetBackground vào nền bộ khung và mã thông báo có chứa màu onSurface vào văn bản TitleBar cũng như các thành phần văn bản. Để cập nhật kiểu văn bản, bạn cần nhập lớp Glance TextStyle. Để cập nhật nền bộ khung, bạn cần đặt thuộc tính backgroundColor của Scaffold thành GlanceTheme.colors.widgetBackground.

Để bắt đầu, hãy làm theo các bước sau:

  1. Đưa một nội dung nhập mới vào tệp SociaLiteAppWidget.kt.
//Add to the imports section of your Kotlin code.
import androidx.glance.text.TextStyle
  1. Cập nhật thành phần kết hợp Content của bạn để thêm widgetBackground.
Scaffold(
   titleBar = {
       TitleBar(
           textColor = GlanceTheme.colors.onSurface,
           startIcon = ImageProvider(R.drawable.ic_launcher_monochrome),
           title = "SociaLite",
       )
   },
   backgroundColor = GlanceTheme.colors.widgetBackground,
   modifier = GlanceModifier.fillMaxSize(),
) {
   Text(text = "Hello World", style = TextStyle(color = GlanceTheme.colors.onSurface))
}
  1. Để xem các bản cập nhật, hãy chạy lại ứng dụng rồi thêm một bản sao mới của tiện ích vào màn hình chính của bạn.

Bản sao này sẽ khớp giao diện của các tiện ích khác trên màn hình chính và tự động cập nhật màu nếu bạn thay đổi nền hoặc đặt chế độ tối. Với một hình nền sặc sỡ, tiện ích sẽ điều chỉnh theo phần hình nền ở bên dưới tiện ích đó.

Màn hình chính của Android có tiện ích Xin chào ở giao diện sángMàn hình chính ở giao diện sáng

Màn hình chính của Android có tiện ích Xin chào ở giao diện tốiMàn hình chính ở giao diện tối

Thêm trạng thái trống

Bây giờ, bạn cần cân nhắc về trạng thái và cách định cấu hình tiện ích của mình. Khi bạn thêm một tiện ích vào màn hình chính và cần định cấu hình, tốt nhất là bạn nên hiển thị trạng thái trống. Trạng thái trống sẽ nhắc người dùng định cấu hình tiện ích. Bạn cần thêm một hoạt động liên quan đến cấu hình vào tiện ích của mình cũng như đường liên kết đến hoạt động đó từ trạng thái trống.

Lớp học lập trình này cung cấp các lớp để lưu trữ, truy cập và sửa đổi trạng thái cấu hình của tiện ích. Bạn sẽ thêm mã để cập nhật giao diện người dùng của tiện ích nhằm hiển thị trạng thái này và tạo một thao tác lambda để xử lý thao tác nhấn của người dùng.

Xem mô hình tiện ích

Hãy dành chút thời gian để xem các lớp trong gói com.google.android.samples.socialite.widget.model,

bao gồm lớp WidgetModel, lớp WidgetModelDao và các lớp WidgetModelRepository. Các lớp này đã xuất hiện trong mã khởi đầu của lớp học lập trình và xử lý việc lưu giữ trạng thái của các tiện ích theo cơ sở dữ liệu Room cơ bản. Ngoài ra, các lớp này cũng sử dụng Hilt để quản lý vòng đời của chúng.

Lớp WidgetModel chứa một widgetId do Android chỉ định, contactId của người liên hệ SociaLite mà phần tử này hiển thị, một displayNamephoto sẽ hiển thị, cũng như một Boolean nếu người liên hệ đó có thông báo chưa đọc. Những thông tin này được các thành phần kết hợp SociaLiteAppWidget sử dụng và hiển thị trên tiện ích.

WidgetModelDao là một đối tượng truy cập dữ liệu tóm tắt quyền truy cập vào cơ sở dữ liệu của SociaLite. WidgetModelRepository cung cấp các hàm thuận tiện để tạo, đọc, cập nhật và xoá thực thể của WidgetModel. Những lớp này được tạo bằng Hilt và được chèn vào ứng dụng bằng tính năng chèn phần phụ thuộc.

  • Mở tệp WidgetModel.kt trong gói model được tìm thấy ở app/src/main/java/com/google/android/samples/socialite/widget/model/.

Đây là lớp data có một chú giải Entity. Mỗi thực thể tiện ích được Android chỉ định một giá trị nhận dạng riêng và SociaLite dùng giá trị nhận dạng này làm khoá chính cho dữ liệu mô hình. Mỗi thực thể của mô hình theo dõi thông tin cơ bản của người liên hệ được chỉ định và liệu có thông báo chưa đọc từ người liên hệ này hay không.

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Contact::class,
            parentColumns = ["id"],
            childColumns = ["contactId"],
            onDelete = ForeignKey.CASCADE,
        ),
    ],
    indices = [
        Index("widgetId"),
        Index("contactId"),
    ],
)
data class WidgetModel(
    @PrimaryKey val widgetId: Int,
    val contactId: Long,
    val displayName: String,
    val photo: String,
    val unreadMessages: Boolean = false,
) : WidgetState

Trạng thái trống

Bạn muốn thành phần kết hợp Content tải mô hình tiện ích từ WidgetModelRepository và hiển thị trạng thái trống nếu không có mô hình nào. Nếu không, bạn muốn hiển thị nội dung bình thường của tiện ích. Hiện tại, nội dung này sẽ là thông báo "Hello World" (Xin chào) nhưng trong phần tiếp theo, bạn cần tạo một giao diện đẹp hơn.

Bạn sẽ thay thế thành phần kết hợp Content bằng biểu thức when hiển thị thành phần kết hợp ZeroState hoặc phần giữ chỗ Text.

  1. Trong phương thức provideGlance, bên ngoài thành phần kết hợp, hãy lấy thông tin tham chiếu đến WidgetModelRepository và giá trị nhận dạng hiện tại của tiện ích. Thêm các dòng sau đây trước provideContent trong phương thức SociaLiteAppWidget provideGlance.
override suspend fun provideGlance(context: Context, id: GlanceId) {
   val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id)
   val repository = WidgetModelRepository.get(context)

Bạn có thể cũng cần thêm các mục nhập sau đây:

import com.google.android.samples.socialite.widget.model.WidgetModel
import com.google.android.samples.socialite.widget.model.WidgetModelRepository
import com.google.android.samples.socialite.widget.model.WidgetState.Loading
import androidx.glance.appwidget.GlanceAppWidgetManager
  1. Trong hàm có khả năng kết hợp Content, hãy thêm kho lưu trữ và giá trị nhận dạng tiện ích dưới dạng tham số và dùng những giá trị này để tải mô hình của bạn. Cập nhật chữ ký hàm của thành phần kết hợp Content và thêm dòng sau đây:
private fun Content(repository: WidgetModelRepository, widgetId: Int) {
   val model = repository.loadModel(widgetId).collectAsState(Loading).value
  1. Nếu Android Studio không tự động thêm mục nhập sau đây, thì bạn có thể thêm theo cách thủ công.
import androidx.compose.runtime.collectAsState

Bạn cũng cần cập nhật provideGlance để truyền giá trị nhận dạng tiện ích và kho lưu trữ sang Content.

Thay thế provideGlance bằng thông tin sau đây:

override suspend fun provideGlance(context: Context, id: GlanceId) {
        val widgetId = GlanceAppWidgetManager(context).getAppWidgetId(id)
        val repository = WidgetModelRepository.get(context)

        provideContent {
            GlanceTheme {
                Content(repository, widgetId)
            }
        }
    }
  1. Trong hàm có khả năng kết hợp Content, hãy xác định trạng thái cần hiển thị dựa trên việc mô hình có tồn tại hay không. Di chuyển Scaffold và nội dung tiện ích vào thành phần kết hợp ZeroState bằng cách thay thế thành phần Scaffold và nội dung của thành phần đó bằng thông tin sau đây khi chặn:
   when (model) {
       is WidgetModel -> {Text("Hello World")}
       else -> ZeroState(widgetId)
   }

Thành phần kết hợp ZeroState đã sẵn sàng để thêm vào mã khởi đầu trong gói com.google.android.samples.socialite.widget.ui.

  1. Nếu Android Studio không tự động nhập gói com.google.android.samples.socialite.widget.ui, thì bạn có thể thêm đoạn mã sau đây vào phần mục nhập SociaLiteAppWidget.
import com.google.android.samples.socialite.widget.ui.ZeroState
  1. Để xem các bản cập nhật, hãy chạy lại ứng dụng rồi thêm một bản sao mới của tiện ích vào màn hình chính của bạn. Bạn sẽ thấy tiện ích hiển thị thành phần ZeroState và một Nút. Khi bạn nhấp vào, Nút này sẽ mở hoạt động liên quan đến cấu hình và trong phần tiếp theo, bạn sẽ cập nhật trạng thái tiện ích từ hoạt động này.

Màn hình chính của Android minh hoạ Trạng thái trống của tiện ích SociaLite. Trạng thái trống có một nút duy nhất.

Hoạt động liên quan đến cấu hình của SociaLite AppWidget hiển thị 4 người liên hệ để chọn.

Hoạt động liên quan đến cấu hình

Xem Hàm có khả năng kết hợp ZeroState. Hàm này có trong gói com.google.android.samples.socialite.widget.ui của tệp ZeroState.kt.

@Composable
fun ZeroState(widgetId: Int) {
   val widgetIdKey = ActionParameters.Key<Int>(AppWidgetManager.EXTRA_APPWIDGET_ID)
Scaffold(
   titleBar = {
       TitleBar(
           modifier = GlanceModifier.clickable(actionStartActivity(MainActivity::class.java)),
           textColor = GlanceTheme.colors.onSurface,
           startIcon = ImageProvider(R.drawable.ic_launcher_monochrome),
           title = "SociaLite",
       )
   },
   backgroundColor = GlanceTheme.colors.widgetBackground,
   modifier = GlanceModifier.fillMaxSize(),
) {
   Box(modifier = GlanceModifier.fillMaxSize(), contentAlignment = Alignment.Center) {
       Button(
           text = "Select Favorite Contact",
           onClick = actionStartActivity<SociaLiteAppWidgetConfigActivity>(
               parameters = actionParametersOf(widgetIdKey to widgetId),
           ),
       )
   }
}

}

Thành phần kết hợp Scaffold đã được di chuyển vào thành phần kết hợp ZeroState. TitleBar có đối tượng sửa đổi clickable có thể mở hoạt động chính của SociaLite. ZeroState sử dụng thành phần kết hợp Button của Glance để hiển thị lời kêu gọi hành động cho người dùng và khi nhấp vào, lời kêu gọi này sẽ mở hoạt động SociaLiteAppWidgetConfigActivity cũng như thêm giá trị nhận dạng tiện ích dưới dạng ý định bổ sung. Cả hai thao tác này đều sử dụng hàm tiện lợi actionStartActivity của Glance. Để biết thêm thông tin về các thao tác, hãy xem bài viết Xử lý hoạt động tương tác của người dùng.

  1. Xem cách SociaLiteAppWidgetConfigActivity được dùng để cập nhật cấu hình tiện ích của bạn. Lớp này cũng là hoạt động cấu hình trong tiện ích. Hoạt động cấu hình đọc số nguyên ý định bổ sung bằng khoá AppWidgetManager.*EXTRA_APPWIDGET_ID.*Để biết thêm thông tin về các hoạt động cấu hình, hãy xem bài viết Cho phép người dùng định cấu hình tiện ích ứng dụng.
  2. Trong SociaLiteAppWidgetConfigActivity, thay thế TODO trong thuộc tính ContactRow onClick bằng đoạn mã sau đây:
{
  coroutineScope.launch {

     widgetModelRepository.createOrUpdate(
       WidgetModel(
           appWidgetId,
           contact.id,
           contact.name,
           contact.iconUri.toString(),
           false,
       ),
     )
     SociaLiteAppWidget().updateAll(this@SociaLiteAppWidgetConfigActivity)
     val resultValue = Intent().putExtra(
       AppWidgetManager.EXTRA_APPWIDGET_ID,
       appWidgetId,
     )
     setResult(RESULT_OK, resultValue)
     finish()
  }
}

Thêm các dữ liệu được bao gồm sau đây nếu Android Studio không tự động thêm

import com.google.android.samples.socialite.widget.model.WidgetModel
import androidx.glance.appwidget.updateAll
import kotlinx.coroutines.launch

Khối mã này sẽ cập nhật trạng thái tiện ích của bạn. Trước tiên, khối mã này sử dụng kho lưu trữ để lưu hoặc cập nhật WidgetModel bằng thông tin của người liên hệ đã chọn. Sau đó, khối mã này sẽ gọi hàm tạm ngưng updateAll. Hàm này sẽ cập nhật mọi tiện ích trên màn hình chính và có thể được gọi từ vị trí bất kỳ trong ứng dụng. Cuối cùng, khối mã này đặt kết quả của hoạt động liên quan đến cấu hình để báo hiệu rằng đã cập nhật tiện ích thành công.

  1. Chạy và thay thế tiện ích của bạn trên màn hình chính. Bạn sẽ thấy trạng thái ban đầu mới.

Màn hình chính của Android minh hoạ Trạng thái trống của tiện ích SociaLite. Trạng thái trống có một nút duy nhất.

  1. Nhấp vào Select favorite contact (Chọn người liên hệ yêu thích). Thao tác này sẽ đưa bạn đến hoạt động liên quan đến cấu hình.

Ảnh chụp màn hình hoạt động liên quan đến cấu hình hiển thị 4 người liên hệ để chọn Màn hình chính của Android có tiện ích Xin chào ở giao diện sáng

  1. Chọn một người liên hệ. Thao tác này sẽ cập nhật tiện ích của bạn. Tuy nhiên, tiện ích chưa hiển thị người liên hệ yêu thích của bạn vì bạn sẽ thêm chức năng đó trong phần tiếp theo.

Quản lý dữ liệu tiện ích

  1. Mở công cụ App inspection (Kiểm tra ứng dụng), kết nối với một quy trình (nếu cần), sau đó chọn thẻ Database inspector (Trình kiểm tra cơ sở dữ liệu) để xem nội dung trong cơ sở dữ liệu ứng dụng.
  2. Chọn một người liên hệ yêu thích trong tiện ích và xem tiện ích cập nhật thành "Xin chào". Quay lại công cụ App Inspection (Kiểm tra ứng dụng), bạn sẽ thấy thẻ Widget model (Mô hình tiện ích) với một mục nhập cho tiện ích của bạn. Bạn sẽ cần làm mới bảng này hoặc nhấn vào Thông tin cập nhật trực tiếp để xem các thay đổi.

dd030cce6a75be25.png

  1. Thêm một tiện ích khác và chọn người liên hệ khác. Bạn cần nhấn vào Làm mới bảng hoặc Thông tin cập nhật trực tiếp để xem mô hình mới.
  2. Xoá tiện ích và lưu ý rằng mô hình đã được để lại trong cơ sở dữ liệu sau khi tiện ích bị xoá.

Khi xoá một tiện ích, bạn có thể cập nhật SociaLiteAppWidgetReceiver để dọn dẹp cơ sở dữ liệu bằng cách ghi đè onDeleted.

Để dọn dẹp mô hình tiện ích mất nguồn gốc, bạn có thể gọi WidgetModelRepository.cleanupWidgetModels. Lớp kho lưu trữ được quản lý bằng Hilt và bạn cần sử dụng tính năng chèn phần phụ thuộc để truy cập vào thực thể của lớp này.

  1. Trong SociaLiteAppWidgetReceiver, hãy thêm chú giải Hilt AndroidEntryPoint vào phần khai báo lớp phần tử nhận của bạn và chèn thực thể WidgetModelRepository.
  2. Gọi WidgetModelRepository.cleanupWidgetModels trong phương thức ghi đè onDeleted.

Mã của bạn sẽ có dạng như thế này:

package com.google.android.samples.socialite.widget

import android.content.Context
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import com.google.android.samples.socialite.widget.model.WidgetModelRepository
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class SociaLiteAppWidgetReceiver : GlanceAppWidgetReceiver() {
   override val glanceAppWidget: GlanceAppWidget = SociaLiteAppWidget()

   @Inject
   lateinit var repository: WidgetModelRepository

   override fun onDeleted(context: Context, appWidgetIds: IntArray) {
       super.onDeleted(context, appWidgetIds)
       repository.cleanupWidgetModels(context)
   }

}
  1. Chạy lại ứng dụng. Khi xoá tiện ích khỏi màn hình chính, bạn sẽ cần xem hàng mô hình đã bị xoá trong công cụ App Inspector (Kiểm tra ứng dụng) hay chưa.

5. Thêm Giao diện người dùng cho người liên hệ và cập nhật khi có thông báo mới

Bạn đã đến phần cuối cùng trong lớp học lập trình này. Trong phần này, bạn sẽ triển khai giao diện người dùng cuối cùng cho người liên hệ trong tiện ích của mình và cập nhật tiện ích khi người liên hệ đó có thông báo chưa đọc.

  1. Xem lại lớp WidgetModelRepository trong gói model.

Tại đây, bạn sẽ có phương thức tiện lợi updateUnreadMessagesForContact, phương thức này cập nhật các tiện ích được liên kết bằng giá trị nhận dạng của một người liên hệ.

//Don't add this code.
fun updateUnreadMessagesForContact(contactId: Long, unread: Boolean) {
   coroutineScope.launch {
       widgetModelDao.modelsForContact(contactId).filterNotNull().forEach { model ->
           widgetModelDao.update(
             WidgetModel(model.widgetId, model.contactId, model.displayName, model.photo, unread)
           )
           SociaLiteAppWidget().updateAll(appContext)
       }
   }
}

Phương thức này có 2 tham số: contactId, giá trị nhận dạng của người liên hệ được cập nhật và unread, một Boolean của trạng thái thông báo chưa đọc. Phương thức này dùng WidgetModelDao để tìm mọi mô hình tiện ích hiển thị người liên hệ này và cập nhật mô hình với trạng thái đọc mới. Sau đó, hàm này sẽ gọi phương thức SociaLiteAppWidget().updateAll do Glance cung cấp để cập nhật mọi tiện ích trên màn hình chính của người dùng.

Giờ đây, khi đã hiểu được cách cập nhật tiện ích và trạng thái tiện ích, bạn có thể tạo giao diện người dùng cho người liên hệ, gửi tin nhắn và xem tiện ích cập nhật. Để làm như vậy, bạn cần cập nhật SociaLiteAppWidget bằng một thành phần kết hợp FavoriteContact trong bố cục tiện ích của bạn. Trong bố cục này, bạn cũng cần kiểm tra xem mình nên hiển thị No new messages hay một New Messages!.

  1. Xem lại tệp FavoriteContact.kt trong gói com.google.android.samples.socialite.widget.ui.
//Don't add this code.
@Composable
fun FavoriteContact(model: WidgetModel, onClick: Action) {
   Column(
       modifier = GlanceModifier.fillMaxSize().clickable(onClick)
           .background(GlanceTheme.colors.widgetBackground).appWidgetBackground()
           .padding(bottom = 8.dp),
       verticalAlignment = Alignment.Vertical.Bottom,
       horizontalAlignment = Alignment.Horizontal.CenterHorizontally,
   ) {
       Image(
           modifier = GlanceModifier.fillMaxWidth().wrapContentHeight().defaultWeight()
               .cornerRadius(16.dp),
           provider = ImageProvider(model.photo.toUri()),
           contentScale = ContentScale.Crop,
           contentDescription = model.displayName,
       )
       Column(
           modifier = GlanceModifier.fillMaxWidth().wrapContentHeight().padding(top = 4.dp),
           verticalAlignment = Alignment.Vertical.Bottom,
           horizontalAlignment = Alignment.Horizontal.CenterHorizontally,
       ) {
           Text(
               text = model.displayName,
               style = TextStyle(
                   fontWeight = FontWeight.Bold,
                   fontSize = 24.sp,
                   color = (GlanceTheme.colors.onSurface),
               ),
           )

           Text(
               text = if (model.unreadMessages) "New Message!" else "No messages",
               style = TextStyle(
                   fontWeight = FontWeight.Bold,
                   fontSize = 16.sp,
                   color = (GlanceTheme.colors.onSurface),
               ),
           )
       }
   }
}
  1. Thay thế Text("Hello World") trong thành phần kết hợp Content của SociaLiteAppWidget bằng một lệnh gọi đến thành phần kết hợp FavoriteContact.

Thành phần kết hợp này sẽ nhận WidgetModel và một thao tác do hàm actionStartActivity của Glance tạo.

  1. Thêm lệnh gọi vào khối when của bạn trước ZeroState khi mô hình này không phải là WidgetModel.
when (model) {
  is WidgetModel -> FavoriteContact(model = model, onClick = actionStartActivity(
     Intent(LocalContext.current.applicationContext, MainActivity::class.java)
         .setAction(Intent.ACTION_VIEW)
         .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
         .setData("https://socialite.google.com/chat/${model.contactId}".toUri()))
  )
  else -> ZeroState(widgetId)
}
  1. Nếu Android Studio không tự động thêm các mục nhập, thì hiện bạn có thể làm như sau:
import com.google.android.samples.socialite.widget.ui.FavoriteContact
import androidx.glance.appwidget.action.actionStartActivity
import android.content.Intent
import com.google.android.samples.socialite.MainActivity
import androidx.core.net.toUri
  1. Chạy ứng dụng của bạn.
  2. Chọn một người liên hệ yêu thích, gửi tin nhắn và thoát khỏi ứng dụng ngay trước khi họ trả lời. Khi có tin nhắn trả lời, trạng thái của tiện ích sẽ thay đổi.
  3. Nhấp vào tiện ích để mở cuộc trò chuyện và xem trạng thái cập nhật lại khi bạn thoát khỏi màn hình chính.

Màn hình chính của Android có tiện ích SociaLite đã hoàn thành ở giao diện sáng.

6. Xin chúc mừng

Bạn đã hoàn thành lớp học lập trình này và tìm hiểu cách viết một tiện ích bằng Glance! Bạn có thể thoải mái tạo một tiện ích có giao diện đẹp trên nhiều màn hình chính, xử lý hoạt động đầu vào của người dùng và tự cập nhật tiện ích đó.

Để nhận đoạn mã giải pháp trong nhánh main, hãy làm theo các bước sau:

  1. Nếu bạn đã tải ứng dụng SociaLite xuống, hãy chạy lệnh này:
git checkout main
  1. Nếu không, hãy tải lại đoạn mã xuống để xem nhánh main:
git clone git@github.com:android/socialite.git

Tìm hiểu thêm