Nền tảng ứng dụng

Tạo điều kiện cho hoạt động tương tác liền mạch trên nhiều thiết bị

Loa

Giới thiệu

Giới thiệu về bài thuyết trình

Khi người dùng chuyển đổi giữa điện thoại, thiết bị đeo và thiết bị ngoại vi âm thanh, việc mang lại trải nghiệm mượt mà trên nhiều thiết bị đã trở thành yếu tố thiết yếu để thu hút người dùng. Tuy nhiên, việc dựa vào các phương pháp kết nối mạng tiêu chuẩn thường dẫn đến tình trạng hao pin, tăng đột biến độ trễ và phân mảnh phiên người dùng.

Bài viết chuyên sâu này giới thiệu các API Android chuyên biệt được xây dựng cho hoạt động giao tiếp giữa nhiều thiết bị, cung cấp các mẫu tiết kiệm bộ nhớ và năng lượng để giúp quá trình chuyển đổi giữa các thiết bị diễn ra tức thì, đáng tin cậy và trực quan.

Tóm tắt

  • Trình quản lý thiết bị đồng hành thay thế dịch vụ nền trước cho thiết bị ngoại vi Bluetooth bằng cách đánh thức ứng dụng dựa trên sự hiện diện và quản lý kết nối mà không làm hao pin. Android 17 bổ sung các hồ sơ mới dành riêng cho thiết bị y tế và thiết bị theo dõi thể chất.
  • Jetpack Telecom đóng vai trò là trình điều phối cuộc gọi hệ thống cho các ứng dụng VoIP trên các thiết bị thông minh được kết nối như đồng hồ và ô tô, hoạt động trở lại Android 8. Lớp này cũng đồng bộ hoá các thao tác tắt tiếng và gác máy thông qua CallControlScope.
  • Các tính năng sắp ra mắt của Jetpack Telecom 1.1.0 bao gồm nhật ký cuộc gọi hợp nhất trong trình quay số hệ thống (SDK 36.1 trở lên) và tính năng tự động phân chia mạng 5G trên các mạng di động được hỗ trợ.
  • SDK Chia sẻ nhanh sẽ ra mắt vào năm tới để nhà phát triển có thể nhúng tính năng chia sẻ tệp cục bộ, được mã hoá hai đầu trực tiếp vào các ứng dụng Android và iOS. Tính năng Chạm để chia sẻ (ra mắt cùng với Pixel 11) thậm chí có thể tự động đánh thức một ứng dụng nhận đang đóng.

Tài nguyên

Gemini Notebook

Chủ đề

  • nhiều thiết bị
  • companion-device-manager
  • jetpack-telecom
  • chia sẻ nhanh
  • bluetooth
  • voip
  • ngang hàng

Bản chép lời

Giới thiệu về hoạt động tương tác trên nhiều thiết bị

  • Chào các bạn!

  • Chào mừng bạn đến với bài học Tạo điều kiện cho hoạt động tương tác liền mạch trên nhiều thiết bị.

  • Tôi là Hanrui, và hôm nay tôi có hai đồng nghiệp là Yacine và Nataraj cùng tham gia.

  • Khi nói về khả năng kết nối, chúng ta thường nghĩ đến một đường liên kết mạng cơ bản hoặc tai nghe Bluetooth.

  • Trên Android, khả năng kết nối còn rộng hơn thế nhiều.

  • Mục đích thực sự là giúp các thiết bị hoạt động hiệu quả hơn cùng nhau.

  • Hôm nay, chúng ta sẽ tìm hiểu các trường hợp phổ biến trên nhiều thiết bị và cho bạn biết API nào phù hợp với từng trường hợp.

  • Trong một thời gian khá dài, chúng ta thường xây dựng ứng dụng cho một thiết bị duy nhất, chỉ dành cho điện thoại.

  • Sau đó, thiết bị đeo trở nên phổ biến.

  • Chẳng bao lâu sau, việc ghép nối đồng hồ thông minh với điện thoại, đồng bộ hoá dữ liệu ở chế độ nền hoặc nhận cuộc gọi trực tiếp đã trở nên rất phổ biến.

  • Thêm tai nghe – thêm tai nghe không dây.

  • Giờ đây, người dùng có thể chuyển đổi thiết bị âm thanh giữa cuộc gọi, từ đồng hồ sang điện thoại, tai nghe hoặc ô tô.

  • Gần đây, nhiều người dùng cũng mang theo một thiết bị màn hình lớn thứ hai, chẳng hạn như thiết bị gập hoặc máy tính bảng.

  • Họ muốn ứng dụng hoạt động mượt mà trên các thiết bị này.

  • Và điều đó cũng áp dụng cho máy tính xách tay và Chromebook, nơi người dùng có thể chạy ứng dụng Android và duy trì kết nối ngay tại bàn làm việc.

  • Ngay cả vào năm 2023, một người dùng trung bình sở hữu từ 3 đến 4 thiết bị.

  • Người dùng mong đợi nhiều hơn là chỉ chức năng độc lập trên các thiết bị này.

  • Họ tìm kiếm một trải nghiệm kết hợp đặc biệt hơn.

  • Trải nghiệm kết hợp này đặc biệt rõ ràng khi mọi người ở cùng một phòng như chúng ta hôm nay và muốn có một cách dễ dàng để chia sẻ ảnh, đường liên kết hoặc ứng dụng với những người bên cạnh.

  • Nhìn vào các con số, hệ sinh thái này rất lớn và đang phát triển nhanh chóng.

  • Trong năm nay, có khoảng 22 tỷ thiết bị IoT trên toàn thế giới, tăng 14% so với cùng kỳ năm trước.

  • Trong số tất cả các điểm cuối này, Wi-Fi chiếm 1/3 và Bluetooth chiếm 1/4.

  • Khả năng kết nối giữa các thiết bị không còn là một trường hợp hiếm gặp.

  • Nó là một phần trong cuộc sống hằng ngày của chúng ta.

  • Tuy nhiên, hiện nay, hầu hết các ứng dụng vẫn xây dựng tính năng trên nhiều thiết bị dựa trên mạng cấp thấp và API Bluetooth, điều này tạo ra ba thách thức.

  • Đầu tiên là pin và bộ nhớ.

  • Việc duy trì các dịch vụ và hoạt động quét trong nền sẽ làm tiêu hao pin và bộ nhớ.

  • Thứ hai là độ trễ.

  • Độ trễ không thể dự đoán làm gián đoạn các cuộc gọi và quá trình đồng bộ hoá dữ liệu theo thời gian thực.

  • Và thứ ba là rào cản kết nối.

  • Việc quản lý quá trình khám phá, ghép nối và kết nối lại trên tất cả thiết bị là một việc phức tạp và dễ dẫn đến trải nghiệm người dùng bị gián đoạn.

  • Để giải quyết những vấn đề đó, chúng tôi đã xây dựng các API chuyên dụng trên ba lĩnh vực chính để giúp các tính năng trên nhiều thiết bị trở nên thực sự đơn giản.

  • Tôi sẽ bắt đầu với các ứng dụng đồng hành, sau đó Nataraj sẽ trình bày về các ứng dụng VoIP và sau đó Yacine sẽ nói về tính năng chia sẻ ngang hàng (P2P).

Tổng quan về ứng dụng đồng hành

  • Trước tiên, hãy cùng tìm hiểu về ứng dụng đồng hành.

  • Ứng dụng đồng hành là ứng dụng bạn cài đặt trên điện thoại để thiết bị đồng hành (chẳng hạn như đồng hồ thông minh hoặc cảm biến đo đường huyết) có thể hoạt động hiệu quả với điện thoại.

  • Trong phần này, chúng ta sẽ tập trung vào các thiết bị kết nối qua Bluetooth.

  • Thường có 4 danh mục thiết bị đồng hành.

  • Đầu tiên là thiết bị y tế, chẳng hạn như cảm biến glucose và máy theo dõi dấu hiệu sinh tồn.

  • Và thiết bị đeo, như đồng hồ thông minh và vòng đeo tay thể dục.

  • Tai nghe, có tính năng Ghép nối nhanh và các chế độ điều khiển thông minh.

  • Và bộ phụ kiện ô tô, cho những tính năng như chìa khoá kỹ thuật số cho ô tô và gọi điện rảnh tay.

  • Mỗi danh mục đều có hành trình riêng của người dùng chính, nhưng tất cả đều phụ thuộc vào kết nối đáng tin cậy giữa thiết bị và điện thoại.

  • Đối với thiết bị y tế, kết nối đó thậm chí có thể ảnh hưởng đến tính mạng.

  • Hãy cùng tìm hiểu 4 nhu cầu phổ biến mà chúng tôi nhận thấy trong các danh mục này.

  • Đầu tiên là quản lý thiết bị.

  • Ứng dụng cần tìm đúng thiết bị trong số mọi thiết bị ở gần, chẳng hạn như chiếc đồng hồ ở đây, chứ không phải tai nghe hoặc loa, rồi bắt đầu quy trình ghép nối để kết nối.

  • Thứ hai là kiểm tra sự hiện diện.

  • Ứng dụng cần biết khi nào thiết bị ở gần và khi nào thiết bị không còn ở gần.

  • Và khi thiết bị xuất hiện, ứng dụng cần có cách để được đánh thức để có thể thực hiện công việc của mình ngay cả khi người dùng chưa mở ứng dụng.

  • Thứ ba, duy trì kết nối.

  • Sau khi kết nối, các ứng dụng muốn kết nối đó phải đáng tin cậy và ổn định.

  • Nếu kết nối bị ngắt, chẳng hạn như người dùng đi ra khỏi phạm vi rồi quay lại, thì ứng dụng cần tự động kết nối lại, ngay cả khi ứng dụng không chạy vào thời điểm đó.

  • Thứ tư là đồng bộ hoá và chuyển dữ liệu.

  • Đây mới là giá trị thực sự.

  • Đồng bộ hoá chỉ số cảm biến, đẩy bản cập nhật chương trình cơ sở hoặc gửi chế độ cài đặt đến thiết bị.

  • Đối với thiết bị y tế, điều này có thể rất quan trọng.

  • Nếu bỏ lỡ một lần đọc, bạn có thể bỏ lỡ một cảnh báo.

  • Tất cả các phương thức này đều cần quy trình ứng dụng hoạt động và kết nối đúng thời điểm.

  • Vậy các ứng dụng đã đáp ứng tất cả những nhu cầu này như thế nào?

  • Theo truyền thống, các ứng dụng phụ thuộc nhiều vào dịch vụ nền trước.

  • Dưới đây là cách triển khai thông thường.

  • Nếu đã từng tạo ứng dụng đồng hành, bạn có thể thấy quen thuộc với điều này.

  • Bạn khai báo quyền quét và kết nối Bluetooth, quyền sử dụng dịch vụ trên nền trước và loại dịch vụ trên nền trước của thiết bị đã kết nối, sau đó khai báo quyền gửi thông báo vì dịch vụ trên nền trước phải hiển thị thông báo liên tục.

    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
                     android:usesPermissionFlags="neverForLocation" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
  • Sau đó, bạn chạy một dịch vụ cố định để duy trì quy trình của ứng dụng.

    class BleForegroundService : Service() {
       override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
          …
          return START_STICKY
       }
    }
  • Bạn biết đấy, cách này có hiệu quả nhưng lại tốn pin và bộ nhớ vì dịch vụ chạy liên tục, ngay cả khi thiết bị không ở gần.

  • Đó là lý do chúng tôi xây dựng Trình quản lý thiết bị đồng hành, hay gọi tắt là CDM.

  • Trước tiên, API này có thể đánh thức ứng dụng của bạn dựa trên sự hiện diện của thiết bị và miễn là thiết bị vẫn nằm trong phạm vi, hệ thống sẽ duy trì quy trình của ứng dụng.

  • Không cần dịch vụ nền trước.

  • Thứ hai, CDM hỗ trợ hồ sơ thiết bị.

  • Với hồ sơ thiết bị, người dùng cấp tất cả các quyền mà ứng dụng của bạn cần chỉ bằng một lần nhấn thay vì một chuỗi hộp thoại cấp quyền.

  • Điều này giúp cải thiện đáng kể trải nghiệm người dùng và tỷ lệ chuyển đổi.

  • Hồ sơ đồng hồ đã có từ Android 12, kính từ Android 14.

  • Trong Android 17, chúng tôi đã thêm hai hồ sơ mới dành riêng cho thiết bị y tế và thiết bị theo dõi thể chất.

  • Hiện tại, cũng có một số hồ sơ đặc quyền chỉ dành cho ứng dụng hệ thống.

  • Nếu nhận thấy nhu cầu lớn hơn từ hệ sinh thái, chúng tôi có thể cân nhắc cung cấp các tính năng này cho nhiều nhà phát triển hơn.

Triển khai bằng Trình quản lý thiết bị đồng hành

  • Việc sử dụng CDM rất dễ dàng.

  • Hãy lấy thiết bị y tế làm ví dụ.

  • Trước tiên, hãy khai báo các quyền cần thiết: một quyền cho thiết bị y tế, một quyền để chạy ở chế độ nền và một quyền để quan sát sự hiện diện của thiết bị.

    <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_MEDICAL" />
    <uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" />
    <uses-permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE" />
  • Một điều cần lưu ý là không giống như các quyền mà chúng ta vừa thấy khi triển khai dịch vụ nền trước, tất cả các quyền được liệt kê ở đây đều là quyền thông thường, nghĩa là chúng sẽ được cấp khi cài đặt mà không có hộp thoại nhắc.

  • Người dùng chỉ cần phê duyệt mối liên kết.

  • Hãy cùng tìm hiểu cách tạo mối liên kết đó.

  • Tại đây, chúng ta sẽ tạo một yêu cầu liên kết có bộ lọc thiết bị để chỉ thiết bị phù hợp mới xuất hiện.

    val paringRequest = AssociationRequest.Builder()
       .addDeviceFilter(deviceFilter)
       .setSingleDevice(true)
       .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_MEDICAL)
       .build()
    
    companionDeviceManager.associate(paringRequest, ContextCompat.getMainExecutor(this),
       object : CompanionDeviceManager.Callback() {
           override fun onAssociationPending(intentSender: IntentSender) {
               intentSenderLauncher.launch(IntentSenderRequest.Builder(intentSender).build())
           }
    
           override fun onAssociationCreated(associationInfo: AssociationInfo) { … }
    
           override fun onFailure(error: CharSequence?) { … }
       }
    )
  • setSingleDevice thành true vì chúng ta chỉ muốn một kết quả trùng khớp và đặt hồ sơ thành medical.

  • Sau đó, hãy gọi hàm liên kết.

  • CDM xử lý việc quét.

  • Trong onAssociationPending, chúng ta sẽ nhận được IntentSender.

  • Khi bạn chạy ứng dụng, hệ thống sẽ hiện một hộp thoại liên kết.

  • Với hồ sơ y tế, một lần nhấn vào đây không chỉ phê duyệt mối liên kết mà còn cấp tất cả các quyền mà hồ sơ cần, thậm chí bỏ qua chế độ Không làm phiền, điều này rất quan trọng đối với các cảnh báo nghiêm trọng.

  • Sau khi người dùng nhấn vào Cho phép, chúng ta sẽ nhận được onAssociationCreated có AssociationInfo, bao gồm cả mã liên kết.

  • Sau đó, chúng ta có thể quan sát sự hiện diện của thiết bị bằng cách gọi startObservingDevicePresence với mã liên kết đó.

  • Bạn cũng cần triển khai một dịch vụ mở rộng CompanionDeviceService.

    class BleCompanionDeviceService : CompanionDeviceService() {
    
       override fun onDevicePresenceEvent(event: DevicePresenceEvent) {
           when (event.event) {
               DevicePresenceEvent.EVENT_BLE_APPEARED -> onDeviceAppeared(event.associationId)
               DevicePresenceEvent.EVENT_BLE_DISAPPEARED -> onDeviceDisappeared(event.associationId)
           }
       }
    
       private fun onDeviceAppeared(associationId: Int) { … }
    
       private fun onDeviceDisappeared(associationId: Int) { … }
    }
  • Khi thiết bị nằm trong phạm vi, hệ thống sẽ liên kết với dịch vụ của bạn ngay cả khi ứng dụng không chạy.

  • Và hệ thống sẽ gọi onDevicePresenceEvent bằng EVENT_BLE_APPEARED.

  • Sau đó, bạn có thể kết nối và đồng bộ hoá.

  • Khi thiết bị rời đi, bạn sẽ nhận được EVENT_BLE_DISAPPEARED.

  • Sau đó, bạn có thể dọn dẹp.

  • Không có dịch vụ luôn bật và không có thông báo chỉ để duy trì hoạt động.

  • Giờ đây, chúng ta phải thừa nhận rằng CDM có một số hạn chế mà nhà phát triển phải lưu ý.

  • Đầu tiên là phiên bản hệ điều hành.

  • Hầu hết các tính năng của CDM đều có trong Android 12 và 13.

  • Tuy nhiên, Android 16 có một số điểm cải tiến thú vị và Android 17 có các hồ sơ mới.

  • Vì vậy, hãy kiểm tra phiên bản hệ điều hành trong thời gian chạy và duy trì một phương án dự phòng cho các phiên bản cũ.

  • Tuy nhiên, SDK mục tiêu tăng lên mỗi năm và SDK tối thiểu có xu hướng tăng theo.

  • Để giải quyết áp lực về bộ nhớ mà các ứng dụng đang và sẽ gặp phải, chúng tôi đặc biệt khuyến khích các ứng dụng di chuyển từ dịch vụ nền trước sang CDM càng sớm càng tốt.

  • Nếu bạn có trường hợp sử dụng mà CDM hiện không hỗ trợ, hãy cho chúng tôi biết.

  • Thứ hai, hiện tại, CDM hỗ trợ quét trong nền nhưng không hỗ trợ quảng cáo trong nền.

  • Chúng tôi hiếm khi nhận được yêu cầu về việc này.

  • Tuy nhiên, nếu ứng dụng của bạn cần điện thoại hoạt động như một thiết bị ngoại vi và quảng cáo liên tục ở chế độ nền, thì chúng tôi rất muốn tìm hiểu thêm về ứng dụng đó.

  • Thứ ba, CDM sử dụng chế độ quét tiết kiệm pin.

  • Nó đánh đổi một chút khả năng phản hồi theo thời gian thực để tiết kiệm pin và bộ nhớ.

  • Vì vậy, đừng mong đợi thiết bị sẽ thức dậy ngay lập tức.

  • Cuối cùng, không được xem lén tải trọng.

  • Bạn không thể đăng ký một đặc điểm hoặc dữ liệu quảng cáo cụ thể.

  • Nhưng khi thiết bị nằm trong phạm vi, ứng dụng của bạn sẽ được đánh thức.

  • Vì vậy, nó có thể kết nối và kiểm tra tải trọng khi cần.

  • Đó là tất cả thông tin về ứng dụng đồng hành và CDM.

  • Tôi sẽ chuyển sang Nataraj để nói về ứng dụng VoIP.

Thách thức khi gọi VoIP trên nhiều thiết bị

  • CompanionDeviceManager là công cụ phù hợp nếu ứng dụng của bạn sở hữu một thiết bị ngoại vi chuyên dụng và liên tục giao tiếp qua thiết bị đó.

  • Chào các bạn!

  • Tôi là Nataraj K R.

  • Bây giờ, tôi sẽ nói về trường hợp sử dụng trên nhiều thiết bị tiếp theo, đó là gọi điện theo thời gian thực.

  • Ngày nay, cách mọi người xử lý cuộc gọi đang thay đổi.

  • Hơn 40% cuộc gọi được thực hiện qua mạng VoIP.

  • Và Android đang đóng vai trò quan trọng trong việc đó.

  • Trên thực tế, 71% số cuộc gọi đó diễn ra trên thiết bị Android, tức là cứ hai cuộc gọi thì có một cuộc gọi trên Android.

  • Không chỉ là cuộc gọi.

  • Hệ sinh thái thiết bị mà mọi người sở hữu cũng đang phát triển mạnh mẽ.

  • Có hơn 250 triệu ô tô đang chạy Android Auto trên đường và chúng tôi cũng đang cung cấp hơn 1 tỷ thiết bị Bluetooth cho người dùng.

  • Tai nghe, đồng hồ Wear OS, ô tô Android có Android Auto, loa Bluetooth là tất cả những thiết bị mà người dùng sở hữu xung quanh điện thoại và họ muốn cuộc gọi của mình diễn ra liền mạch trên tất cả các thiết bị này.

  • Ví dụ: người dùng sẽ nhận được cuộc gọi, họ sẽ liếc nhìn đồng hồ để biết thông tin người gọi.

  • Họ sẽ trả lời cuộc gọi trên đồng hồ.

  • Họ sẽ tắt tiếng hoặc bật tiếng khi cần, đeo tai nghe và tiếp tục cuộc gọi.

  • Nếu muốn ra ngoài, họ sẽ bước vào xe và mong đợi cuộc gọi của mình chuyển đổi liền mạch sang cabin xe.

  • Vì vậy, điều này đã trở thành một thói quen bình thường đối với người dùng và đây không phải là trải nghiệm cao cấp.

  • Đối với người dùng, đây là một yêu cầu cơ bản và nhà phát triển Android có trách nhiệm cung cấp trải nghiệm liền mạch đó cho người dùng trong các ứng dụng VoIP.

  • Nhưng nếu cố gắng tự xây dựng tất cả những điều này từ đầu, bạn sẽ gặp phải ba vấn đề lớn.

  • Một là, phân mảnh với Blue-- Bluetooth socket.

  • Vì hệ thống không biết rằng cuộc gọi của bạn đang xử lý một cuộc gọi, nên bạn sẽ phải tạo các cổng Bluetooth tuỳ chỉnh và phiên phát nội dung nghe nhìn tuỳ chỉnh để cuộc gọi của bạn có thể được chiếu lên đồng hồ hoặc Android Auto.

  • Vấn đề thứ hai là quyền phát âm thanh.

  • Vì bạn phải xử lý các API AudioManager và API Bluetooth cấp thấp, nên hiện tượng rò rỉ âm thanh sẽ xảy ra và đây không phải là ưu tiên của hệ thống, đúng không?

  • Ứng dụng của bạn đang có cuộc gọi VoIP, nên bất cứ khi nào có cuộc gọi của nhà mạng, cuộc gọi đó sẽ bị gián đoạn và modem sẽ thu âm thanh, khiến ứng dụng của bạn không có âm thanh.

  • Tệ nhất là bất cứ khi nào cuộc gọi kết thúc, quyền phát âm thanh sẽ không được trả lại cho ứng dụng của bạn.

  • Và vấn đề thứ ba là đồng bộ hoá.

  • Người dùng sẽ nhấn vào nút tắt tiếng trên đồng hồ, nhưng giao diện người dùng của điện thoại vẫn cho thấy cuộc gọi ở trạng thái bật tiếng.

  • Điều đó gây ra sự nhầm lẫn lớn.

  • Người dùng sẽ ngắt kết nối cuộc gọi từ vô lăng ô tô, nhưng điện thoại vẫn hiện thông báo cuộc gọi đang diễn ra.

  • Điều này khiến người dùng rất thất vọng khi xảy ra.

Giới thiệu về Jetpack Telecom

  • Vì vậy, để giải quyết chính xác tất cả những vấn đề này, chúng tôi đã xây dựng Jetpack Telecom.

  • Vì vậy, bạn chỉ cần đăng ký cuộc gọi một lần với Jetpack Telecom bằng CallsManager và Jetpack Telecom sẽ đóng vai trò là trình điều phối cuộc gọi hệ thống trung tâm.

  • Lớp này chịu trách nhiệm chiếu cuộc gọi của bạn lên đồng hồ, Android Auto và các nền tảng từ xa khác mà người dùng sở hữu.

  • Thư viện này cũng đảm nhận việc xử lý quyền phát âm thanh.

  • Và quan trọng nhất, tất cả những tính năng này đều hoạt động mượt mà, kể cả trên Android 8.

  • Chiếu cuộc gọi lên màn hình đồng hồ hoặc ô tô chỉ là một nửa câu chuyện, đúng không, vì người dùng rõ ràng muốn kiểm soát trạng thái của cuộc gọi từ các thiết bị này.

  • Họ muốn tắt tiếng cuộc gọi trên đồng hồ hoặc bật tiếng khi cần.

  • Họ muốn gác máy bằng nút trên vô lăng ô tô hoặc bằng cách nhấn đúp vào tai nghe.

  • Vì vậy, Jetpack Telecom sẽ theo dõi các sự kiện phần cứng này và trình bày dưới dạng lệnh gọi lại ở cùng một nơi, đó là CallControlScope.

  • Tất cả những gì bạn cần làm là lắng nghe các lệnh gọi lại này, xử lý công cụ gọi và cập nhật trạng thái cuộc gọi tương ứng.

  • Jetpack Telecom chịu trách nhiệm đồng bộ hoá trạng thái cuộc gọi này giữa các thiết bị.

  • Vì vậy, về cơ bản, thư viện này mang lại lợi ích theo hai nhóm.

  • Một là, rõ ràng, nó giải quyết tất cả các vấn đề mà chúng ta đã nói đến.

  • Vì vậy, thay vì bạn phải xử lý các API AudioManager cấp thấp, thư viện sẽ cung cấp các điểm cuối cuộc gọi khai báo, quản lý tiêu điểm âm thanh và đồng bộ hoá giữa các thiết bị.

  • Thành phần này chịu trách nhiệm chuyển cuộc gọi giữa các thiết bị.

  • Và nó cũng xử lý bất cứ khi nào có cuộc gọi di động.

  • Tuy nhiên, một lợi ích khác của thư viện này là vì được xây dựng dựa trên Kotlin, nên mỗi khi bạn thêm một lệnh gọi bằng Jetpack Telecom, thư viện này sẽ mở ra một phạm vi coroutine Kotlin.

  • Vì vậy, nó tận dụng các coroutine Kotlin và coroutine Kotlin đó vẫn hoạt động chính xác cho đến khi kết thúc cuộc gọi.

  • Vì vậy, mọi công việc bạn đã chạy liên quan đến cuộc gọi sẽ tự động được dọn dẹp ngay khi cuộc gọi bị ngắt kết nối.

  • Vì vậy, bạn sẽ không còn công việc nào bị bỏ sót và vòng đời cuộc gọi cũng được thư viện xử lý.

  • Giờ thì bạn có thể đang nghĩ rằng thư viện này thật tuyệt vời và bạn muốn triển khai nó, đúng không?

  • Và chỉ cần 3 bước đơn giản.

  • Bước 1 là bạn phải khai báo phần phụ thuộc.

  • Sau đó, hãy khai báo quyền cần thiết (MANAGE_OWN_CALLS) rồi tạo thực thể cho đối tượng CallsManager.

    <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
  • Sau khi tạo thực thể cho đối tượng CallsManager, hãy đăng ký ứng dụng của bạn với các chức năng mà bạn muốn cung cấp cho ứng dụng, chẳng hạn như gọi video hoặc truyền trực tuyến cuộc gọi.

    // Initialize CallsManager & register supported capabilities once
    val callsManager = CallsManager(context)
    
    callsManager.registerAppWithTelecom(
        capabilities = CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING or
                       CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING
    )
  • Sau đó, bước số 2.

  • Giờ đây, khi đã đăng ký ứng dụng với Jetpack Telecom, bạn có thể xử lý các cuộc gọi đến và cuộc gọi đi.

  • Để làm việc đó, bạn cần tạo một đối tượng CallAttributesCompat chứa siêu dữ liệu của cuộc gọi, chẳng hạn như displayName của người gọi hoặc người nhận cuộc gọi, địa chỉ (là điểm cuối của người gọi), hướng (chỉ định xem cuộc gọi là cuộc gọi đến hay cuộc gọi đi để Jetpack Telecom có thể hiển thị giao diện người dùng tương ứng trên các nền tảng từ xa này) và sau đó là callType (cho biết đó là cuộc gọi thoại hay cuộc gọi video để bạn có được tài nguyên cần thiết).

    val attributes = CallAttributesCompat(
        displayName = "Kukk Nat",
        address = Uri.parse("tel:+16505550199"),
        direction = CallAttributesCompat.DIRECTION_INCOMING,
        callType = CallAttributesCompat.CALL_TYPE_AUDIO_CALL
    )
    
    callsManager.addCall(
        attributes, onAnswer, onDisconnect, onSetActive, onSetInactive
    ) {
        // Coroutine CallControlScope active for the duration of the call
    }
  • Sau khi bạn chuẩn bị xong đối tượng siêu dữ liệu, hãy thêm đối tượng đó bằng phương thức callsManager.addCall.

  • Thao tác này sẽ mở ra một phạm vi coroutine điều khiển cuộc gọi.

  • Sau khi thêm cuộc gọi, bạn sẽ chuyển sang bước 3.

  • Bước 3 là lắng nghe các hành động mà người dùng sẽ thực hiện trên các nền tảng từ xa đó.

  • Vì vậy, vì có CallControlScope, nên bạn chỉ cần thu thập các luồng Kotlin.

    callsManager.addCall(attributes, onAnswer, onDisconnect, onSetActive, onSetInactive) {
        // 1. React to hardware mute toggles from Watch, Car, or Headset
        launch {
            isMuted.collect { muted -> audioEngine.setMicMuted(muted) }
        }
    
        // 2. Observe available endpoints & request declarative switches
        launch {
            currentCallEndpoint.collect { endpoint -> updateUi(endpoint) }
        }
        requestEndpointChange(preferredBluetoothEndpoint)
    }
  • Bạn thu thập isMuted để phản ứng bất cứ khi nào người dùng tắt tiếng cuộc gọi từ các nền tảng từ xa đó và theo đó, bạn có thể cập nhật trạng thái cuộc gọi của mình.

  • Thu thập currentCallEndpoint để biết điểm cuối mà cuộc gọi đang diễn ra để bạn có thể cập nhật giao diện người dùng cho phù hợp.

  • Bạn có thể cho thấy thiết bị cụ thể đó trên giao diện người dùng của điện thoại.

  • Và bất cứ khi nào người dùng muốn thay đổi từ thiết bị này sang thiết bị khác, chỉ cần gọi phương thức requestEndpointChange với bất kỳ điểm cuối ưu tiên nào mà người dùng muốn.

  • Chỉ đơn giản như vậy thôi.

  • Sau khi hoàn tất 3 bước này, ứng dụng của bạn đã sẵn sàng và đã tích hợp Jetpack Telecom.

  • Nhưng không chỉ có vậy.

  • Với Jetpack Telecom 1.1.0, chúng tôi sẽ mang đến nhiều tính năng hơn, chẳng hạn như nhật ký cuộc gọi hợp nhất, tức là tất cả cuộc gọi VoIP của bạn giờ đây sẽ xuất hiện ngay trên trình quay số của hệ thống.

  • Vì vậy, lợi ích của việc này là người dùng có thể tạo thực thể cho tính năng gọi lại bằng một lần nhấn ngay từ trình quay số hệ thống vào ứng dụng của bạn.

  • Vì vậy, bạn không cần phải tìm kiếm ứng dụng cụ thể của mình trong số tất cả các ứng dụng.

  • Họ chỉ cần gọi lại từ trình quay số hệ thống mà họ đã quen dùng.

  • Nhưng đối với một số người, nhưng nếu bạn, nhưng chúng tôi cũng cung cấp các chế độ kiểm soát chi tiết.

  • Nếu không muốn một cuộc gọi cụ thể được ghi lại vào hệ thống, bạn chỉ cần đặt cờ isLogExcluded thành true bên trong đối tượng CallAttributesCompat.

  • Đối tượng siêu dữ liệu mà tôi đã nói trước đó, bạn chỉ cần đặt giá trị là true và cuộc gọi của bạn sẽ không được ghi lại trong hệ thống và không xuất hiện trong trình quay số của hệ thống.

  • Và tính năng tuyệt vời thứ hai sẽ ra mắt trong phiên bản 1.1.0 là phân chia mạng 5G.

  • Vì vậy, trên các mạng của nhà mạng được hỗ trợ, mạng sẽ cung cấp cho bạn các phân đoạn mạng cao cấp, tức là đối với một trường hợp sử dụng cụ thể, nhà mạng sẽ cung cấp cho bạn một phân đoạn mạng có độ trễ thấp và không bị gián đoạn, nhờ đó bạn sẽ có chất lượng và trải nghiệm tốt hơn.

  • Vì vậy, trên các mạng di động được hỗ trợ, bạn sẽ có sẵn tính năng này mà không cần phải làm gì thêm, thậm chí không cần viết một dòng mã nào.

  • Sau khi tích hợp thư viện, các cuộc gọi của bạn sẽ tận dụng các lát cắt mạng đó và bạn sẽ có độ trễ thấp hơn cũng như hiệu suất tốt hơn.

  • Tuy nhiên, nếu vì lý do nào đó mà bạn không muốn sử dụng tính năng này, thì bạn cũng có thể chọn không sử dụng bằng cách dùng cờ CAPABILITY_OPT_OUT_OF_PREMIUM_NETWORK.

  • Jetpack Telecom đã được các ứng dụng liên lạc hàng đầu trên toàn cầu như Google Meet, WhatsApp, KakaoTalk và Zoho Arattai triển khai.

  • Giờ đây, bạn có thể tích hợp thư viện này vào ứng dụng của mình và mang đến trải nghiệm liền mạch hơn cho người dùng.

  • Sau khi đã giới thiệu về tính năng gọi điện theo thời gian thực, tôi xin nhường lời cho người đồng nghiệp tuyệt vời của mình là Yacine để giới thiệu về tính năng chạm để chia sẻ.

  • Cảm ơn bạn.

Thách thức khi chia sẻ ngang hàng

  • Mọi người ơi, tôi biết các bạn hơi mệt, nhưng đừng lo, đây là phần cuối cùng rồi.

  • Vì vậy, tôi sẽ đề cập đến việc chia sẻ tệp ngang hàng, nhưng chúng ta cũng có thể thực hiện các trường hợp sử dụng khác bằng tính năng chia sẻ ngang hàng.

  • Trong hệ sinh thái di động, chúng tôi nhận thấy có hơn 14 tỷ ảnh và video được chia sẻ trên mạng xã hội và ứng dụng nhắn tin mỗi ngày, một con số khổng lồ.

  • Và hầu hết các tệp đó đều được chia sẻ qua đám mây, vì vậy, bạn chụp ảnh, tải ảnh lên thông qua dịch vụ bạn yêu thích và sau đó, người nhận phải tải ảnh xuống theo cách tương tự ở phía bên kia.

  • Và điều tương tự cũng xảy ra khi bạn cố gắng mời ai đó hoặc một người bạn tham gia mạng xã hội để mời ai đó cộng tác trong một danh sách phát hoặc tài liệu, bạn thường phải nhập tên người dùng hoặc email của họ, việc này mất một chút thời gian trên bàn phím của bạn.

  • Bạn có thể mắc lỗi và đó không phải là một trải nghiệm tuyệt vời, đặc biệt là khi cả hai bạn đang ngồi cạnh nhau, nên bạn sẽ cảm thấy như, ồ, tôi phải gửi qua đám mây, tôi phải chờ đợi.

  • Chỉ là không tốt thôi.

  • Ngày nay, chúng ta đều có những chiếc điện thoại, thiết bị, máy tính bảng, thiết bị gập hiện đại và chụp được những bức ảnh tuyệt đẹp bằng các thiết bị đó.

  • Nhưng khi bạn chia sẻ qua một số mạng xã hội hoặc ứng dụng nhắn tin, rõ ràng là vì vấn đề về hiệu suất và bộ nhớ, ảnh sẽ bị nén, dẫn đến việc trên màn hình tuyệt vời của bạn, ảnh sẽ bị vỡ hạt hoặc không có chất lượng tốt nhất.

  • Đó không phải là trải nghiệm tốt nhất mà bạn muốn có trên thiết bị cao cấp của mình.

  • Thứ hai, các tệp hiện nay thực sự khá lớn.

  • Một bức ảnh có thể dễ dàng đạt đến 10 MB, một video dài 5 phút có thể lên đến 500 MB, nên việc tải lên và tải xuống tất cả những nội dung đó sẽ mất khá nhiều thời gian.

  • Và cuối cùng, ở phía bên kia, bạn phải chờ độ trễ đó.

  • Vì vậy, đôi khi bạn có thể đang đi xe buýt, xe buýt đông đúc và khi xe buýt đang di chuyển, bạn không có độ trễ tốt nhất, vùng phủ sóng mạng tốt nhất và đôi khi quá trình chuyển tệp bị gián đoạn, đôi khi vì độ trễ thực sự dài đó, bạn có thể bị hết thời gian chờ và quá trình chuyển tệp bị huỷ, điều này thực sự gây khó chịu cho người dùng.

  • Vậy nên, thay vì dựa vào đám mây để chia sẻ tất cả các tệp đó và phải chờ đợi, tại sao không sử dụng mạng cục bộ thực tế, như Bluetooth hoặc Wi-Fi?

  • Nhưng nếu bạn tự làm việc đó, rõ ràng là bạn phải xử lý trình quản lý Bluetooth, trình quản lý Wi-Fi, phải viết giao thức của riêng mình, ý tôi là, không phải ai cũng muốn dành thời gian làm việc đó.

  • Nhưng thực ra, Android đã có sẵn một số tính năng.

  • Tính năng này đã được ra mắt và có thể bạn đã nghe nói đến, đó là Chia sẻ nhanh.

Tổng quan về tính năng Chia sẻ nhanh

  • Tính năng Chia sẻ nhanh trên Android cho phép bạn có sẵn lớp trừu tượng này trong hệ thống và bạn chỉ cần chia sẻ tệp với một thiết bị khác mà không cần quản lý tất cả những việc đó.

  • Tính năng này đã có trong trang chia sẻ và sẽ tự động chuyển đổi giữa các giao thức để chọn ra giao thức phù hợp nhất cho thiết bị của bạn. Tính năng này hoạt động trên Marshmallow trở lên, tức là có thể tiếp cận một lượng người dùng rất lớn, đúng không?

  • Cuối cùng, giao diện người dùng và trải nghiệm người dùng quen thuộc mà bạn thấy ở bên cạnh thực sự giúp ích cho người dùng của bạn.

  • Vì vậy, họ biết giao diện người dùng là gì, họ không cần phải tìm hiểu cách sử dụng vì họ đã từng thấy giao diện này trước đây.

  • Nhưng chúng tôi biết các nhà phát triển, họ cũng muốn sở hữu trải nghiệm này.

  • Bạn biết rõ ứng dụng của mình, bạn hiểu người dùng của mình hơn chúng tôi.

  • Vậy tại sao bạn không tích hợp tính năng Chia sẻ nhanh ngay trong ứng dụng của mình?

  • Vì vậy, chúng tôi bắt đầu trao đổi với các nhà phát triển và nhận thấy tính năng Chia sẻ nhanh đã khá phổ biến.

  • Google Play Games có trên Android, Chromebook, gần đây là Google Sách và cả Windows, tức là hơn 3 tỷ thiết bị, một con số khổng lồ.

  • Ngoài ra, chúng tôi cũng trang bị cho Pixel 11 tính năng chạm để chia sẻ.

  • Vì vậy, thay vì phải chọn thiết bị mà bạn muốn chia sẻ, bạn chỉ cần nhấn và nhờ NFC, chúng tôi sẽ trực tiếp thực hiện việc khám phá đó cho bạn.

  • Vì vậy, bạn có thể chọn một bức ảnh, nhưng cũng có thể chọn một tệp, bạn chỉ cần chạm hai thiết bị vào nhau và chúng sẽ được chia sẻ trực tiếp bằng một cú chạm.

  • Và một lần nữa, chúng tôi biết rằng các nhà phát triển muốn sở hữu trải nghiệm này.

  • Chúng tôi biết đây là cách tốt nhất để người dùng có thể sử dụng một tính năng cụ thể, trong trường hợp này là chia sẻ tệp.

  • Vậy tại sao bạn không nhúng tính năng Chia sẻ nhanh vào ứng dụng của mình?

  • Vì vậy, vào năm tới, chúng tôi sẽ cung cấp SDK Chia sẻ nhanh trực tiếp cho bạn.

  • Bạn có thể sử dụng trực tiếp trong ứng dụng của mình và tính năng này đã được tích hợp sẵn tính năng bảo mật, dưới dạng mã hoá hai đầu giữa cả hai thiết bị.

  • Vì quá trình chuyển diễn ra giữa các mạng cục bộ đó, nên bạn sẽ có tốc độ tối đa, rõ ràng là trong Wi-Fi tại nhà, nhưng cả hai bạn cũng có thể tự động tạo điểm phát sóng, bạn không cần phải xử lý việc đó.

  • Và đối với các thiết bị hỗ trợ Wi-Fi Direct, tính năng này đã được tích hợp sẵn.

  • Và những tệp mà bạn đã chụp bằng chiếc camera tuyệt vời của mình sẽ giữ nguyên độ phân giải và chi tiết tốt nhất, nhờ đó bạn có thể xem chúng một cách rõ nét trên màn hình lớn.

  • Và vì tính năng này dựa vào mạng cục bộ, nên bạn có thể sử dụng tính năng này trong tàu điện ngầm, ngay cả ở những nơi sâu nhất, bạn vẫn có mạng để chia sẻ với bạn bè.

  • Ngay cả khi ở trên máy bay, bạn sẽ ở độ cao hàng nghìn mét trên bầu trời, nhưng vẫn có thể chia sẻ các tệp đó.

  • Và cuối cùng, đối với bạn với tư cách là nhà phát triển, đây là một khoản tiết kiệm chi phí rất lớn về mặt dữ liệu, vì trước tiên, người dùng của bạn không phải sử dụng gói dữ liệu đó và đây là một vấn đề lớn khi họ đi du lịch với tư cách là khách du lịch.

  • Nhưng bạn cũng không cần phải duy trì hệ thống đó trên máy chủ của riêng mình.

  • Năm ngoái, chúng tôi đã thông báo cho nhà phát triển và người dùng rằng họ có thể dùng tính năng Chia sẻ nhanh để chia sẻ tệp với thiết bị iPhone thông qua AirDrop.

  • Giờ đây, chúng tôi cũng mang đến cho nhà phát triển tính năng Chia sẻ nhanh tương tự.

  • Chúng tôi sẽ cung cấp SDK Chia sẻ nhanh cho iOS để các nhà phát triển có ứng dụng iOS có thể chia sẻ tệp giữa cả hai nền tảng, bất kể thiết bị.

  • Đó là phần trình bày, giờ hãy xem nhanh đoạn mã.

  • Chúng tôi cố gắng làm cho quy trình này trở nên đơn giản nhất có thể.

  • Bạn bắt đầu một QuickShareClient, đính kèm các tệp muốn chia sẻ, rồi sau khi bắt đầu phiên, bạn sẽ thấy cửa sổ phụ đẹp mắt này ở bên cạnh, bảng dưới cùng nơi người dùng có thể xem trước các tệp đang được gửi. Nếu cho rằng nội dung đó nhạy cảm, bạn cũng có thể bật chế độ đó bằng cách chọn tuỳ chọn trên màn hình.

    // Final API may undergo further updates
    val quickShareClient = QuickShareClient.create(activity)
    val attachment = FileAttachment(
      fileName = "report.pdf",
      size = 1_048_576L,
      uri = fileUri,
      mimeType = "application/pdf",
      // Sensitive content will be masked in preview
      isSensitiveContent = false,
    )
    
    val content = ShareableContent.withFiles(listOf(attachment))
    val request = SharingRequest.withContent(content)
    val session = quickShareClient.startSharingSession(request, lifecycleOwner)
  • Khi bạn muốn nhận tệp, một lần nữa, trong ứng dụng của bạn, cũng tương tự như vậy, tất nhiên, trong trường hợp này, bạn không thêm tệp đính kèm, và sau đó ngay khi phiên bắt đầu, bạn không phải xử lý bất kỳ điều gì trong số đó.

    // Final API may undergo further updates
    val quickShareClient = QuickShareClient.create(activity)
    val request = SharingRequest.withoutContent()
    val session = quickShareClient.startSharingSession(request, lifecycleOwner)
    
    // Open the Quick Share overlay in receive mode
    session.launchWindow(activity, contentSelectionIntent = null)
  • Cho đến nay, bạn đã thấy chỉ có 20 dòng mã ở cả hai bên.

  • Chúng tôi biết rằng với tính năng Chia sẻ nhanh và chạm để chia sẻ, khả năng chia sẻ tệp chỉ bằng cách chạm vào thiết bị cũng thực sự mang đến một trường hợp sử dụng mới.

  • Ứng dụng của bạn có thể bị đóng trên thiết bị kia và bằng cách nhấn, ngay khi tệp được chuyển, ứng dụng của bạn sẽ tự động mở và bạn sẽ nhận được URI để có thể thực hiện một số thao tác xử lý trên thiết bị, chẳng hạn như hiển thị nội dung nào đó cho người dùng ngay trong ứng dụng của bạn.

    // Final API may undergo further updates
    val quickShareClient = QuickShareClient.create(activity)
    val urlAttachment = TextAttachment(
      text = groupAddUrl,
      type = TextAttachment.Type.URL,
    )
    
    val content = ShareableContent.withText(urlAttachment)
    val appLink = AppLink("https://myapp.com/group/invite")
    val request = SharingRequest.withContent(content, appLink)
    val session = quickShareClient.startSharingSession(request, lifecycleOwner)
  • Vì vậy, một lần nữa, bạn không cần phải mở ứng dụng trên thiết bị nhận.

  • Khi bạn dùng tính năng chạm để chia sẻ, tệp đó sẽ tự động mở.

  • Và tất nhiên, nếu muốn giao diện người dùng phản ứng với quá trình chuyển tệp đó, bạn chỉ cần có Kotlin Flow và bạn sẽ có tất cả sự kiện để có thể xử lý quá trình đó.

    // Final API may undergo further updates
    scope.launch {
      for (event in session.sharingEvents) {
        when (event) {
          is SharingEvent.SendComplete ->
            println("Send finished")
          is SharingEvent.ReceiveComplete ->
            println("Received, files=${event.content?.files?.size}")
          else -> println("other event")
        }
      }
    }

Tóm tắt và kết thúc

  • Hôm nay, chúng ta đã tìm hiểu rất nhiều điều, nên tôi sẽ tóm tắt lại.

  • Đừng lo, bạn sẽ có vài phút để nghỉ giải lao.

  • CompanionDeviceManager là API phù hợp nhất để bạn xử lý mọi hoạt động tương tác qua Bluetooth, hoạt động tương tác liên tục qua Bluetooth, như Hanrui đã trình bày trước đó.

  • Nếu ứng dụng của bạn có bất kỳ cuộc gọi thoại qua IP nào, thì đó không nhất thiết phải là trường hợp sử dụng chính, ví dụ: bạn có thể có khách hàng hỗ trợ trong ứng dụng của mình, nơi người dùng có thể gọi cho bộ phận hỗ trợ.

  • Trong trường hợp này, một lần nữa, cách tốt nhất để xử lý các cuộc gọi thoại qua IP trên Android là Jetpack Com-- Jetpack Telecom.

  • Cuối cùng, đối với mọi ứng dụng mở hoặc chia sẻ tệp ngang hàng, như tôi đã đề cập, với tính năng chạm để chia sẻ, Chia sẻ nhanh là giải pháp.

  • Cảm ơn mọi người. Trước khi các bạn ra về, tôi muốn thông báo nhanh rằng chúng tôi sẽ ở bên ngoài để trả lời câu hỏi của các bạn.

  • Chúng tôi có khung giờ hỗ trợ nếu bạn có bất kỳ câu hỏi nào, bạn biết đấy, về tất cả các trường hợp sử dụng mà chúng tôi đã trình bày.

  • Và đừng quên chia sẻ những điều bạn học được từ bài nói chuyện của mình hoặc các phiên khác trên mạng xã hội.

  • Chúng tôi có #AndroidDevSummit.

  • Một lần nữa, điều cực kỳ quan trọng đối với chúng tôi là biết rằng chúng tôi có sự tương tác, có những người thực sự thích sự kiện này.

  • Điều này giúp chúng tôi biết rằng chúng tôi có một cộng đồng mà chúng tôi chắc chắn muốn gặp lại.

  • Đừng quên ghé qua quầy thông tin để nhận bùa may mắn mạng xã hội và thêm vào móc khoá robot Android của bạn.

  • Tôi làm vậy có đúng không?

  • Cảm ơn bạn.

Mã

Đoạn mã

Đoạn mã XML ở phút 06:17 trong video "Tổng quan về ứng dụng đồng hành"
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
                 android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Đoạn mã Kotlin ở 06:34 trong phần "Tổng quan về ứng dụng đồng hành"
class BleForegroundService : Service() {
   override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
      …
      return START_STICKY
   }
}
Đoạn mã XML ở 08:03 trong phần "Triển khai bằng Trình quản lý thiết bị đồng hành"
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_MEDICAL" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" />
<uses-permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE" />
Đoạn mã Kotlin ở phút 08:38 trong phần "Triển khai bằng Trình quản lý thiết bị đồng hành"
val paringRequest = AssociationRequest.Builder()
   .addDeviceFilter(deviceFilter)
   .setSingleDevice(true)
   .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_MEDICAL)
   .build()

companionDeviceManager.associate(paringRequest, ContextCompat.getMainExecutor(this),
   object : CompanionDeviceManager.Callback() {
       override fun onAssociationPending(intentSender: IntentSender) {
           intentSenderLauncher.launch(IntentSenderRequest.Builder(intentSender).build())
       }

       override fun onAssociationCreated(associationInfo: AssociationInfo) { … }

       override fun onFailure(error: CharSequence?) { … }
   }
)
Đoạn mã Kotlin ở 09:47 trong phần "Triển khai bằng Trình quản lý thiết bị đồng hành"
class BleCompanionDeviceService : CompanionDeviceService() {

   override fun onDevicePresenceEvent(event: DevicePresenceEvent) {
       when (event.event) {
           DevicePresenceEvent.EVENT_BLE_APPEARED -> onDeviceAppeared(event.associationId)
           DevicePresenceEvent.EVENT_BLE_DISAPPEARED -> onDeviceDisappeared(event.associationId)
       }
   }

   private fun onDeviceAppeared(associationId: Int) { … }

   private fun onDeviceDisappeared(associationId: Int) { … }
}
Đoạn mã XML ở phút 18:29 trong video "Giới thiệu Jetpack Telecom"
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
Đoạn mã Kotlin ở phút 18:36 trong video "Giới thiệu Jetpack Telecom"
// Initialize CallsManager & register supported capabilities once
val callsManager = CallsManager(context)

callsManager.registerAppWithTelecom(
    capabilities = CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING or
                   CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING
)
Đoạn mã Kotlin ở phút 18:55 trong video "Giới thiệu Jetpack Telecom"
val attributes = CallAttributesCompat(
    displayName = "Kukk Nat",
    address = Uri.parse("tel:+16505550199"),
    direction = CallAttributesCompat.DIRECTION_INCOMING,
    callType = CallAttributesCompat.CALL_TYPE_AUDIO_CALL
)

callsManager.addCall(
    attributes, onAnswer, onDisconnect, onSetActive, onSetInactive
) {
    // Coroutine CallControlScope active for the duration of the call
}
Đoạn mã Kotlin ở phút 19:49 trong video "Giới thiệu Jetpack Telecom"
callsManager.addCall(attributes, onAnswer, onDisconnect, onSetActive, onSetInactive) {
    // 1. React to hardware mute toggles from Watch, Car, or Headset
    launch {
        isMuted.collect { muted -> audioEngine.setMicMuted(muted) }
    }

    // 2. Observe available endpoints & request declarative switches
    launch {
        currentCallEndpoint.collect { endpoint -> updateUi(endpoint) }
    }
    requestEndpointChange(preferredBluetoothEndpoint)
}
Đoạn mã Kotlin ở phút 29:31 trong video "Tổng quan về tính năng Chia sẻ nhanh"
// Final API may undergo further updates
val quickShareClient = QuickShareClient.create(activity)
val attachment = FileAttachment(
  fileName = "report.pdf",
  size = 1_048_576L,
  uri = fileUri,
  mimeType = "application/pdf",
  // Sensitive content will be masked in preview
  isSensitiveContent = false,
)

val content = ShareableContent.withFiles(listOf(attachment))
val request = SharingRequest.withContent(content)
val session = quickShareClient.startSharingSession(request, lifecycleOwner)
Đoạn mã Kotlin ở phút 29:57 trong video "Tổng quan về tính năng Chia sẻ nhanh"
// Final API may undergo further updates
val quickShareClient = QuickShareClient.create(activity)
val request = SharingRequest.withoutContent()
val session = quickShareClient.startSharingSession(request, lifecycleOwner)

// Open the Quick Share overlay in receive mode
session.launchWindow(activity, contentSelectionIntent = null)
Đoạn mã Kotlin ở phút 30:23 trong video "Tổng quan về tính năng Chia sẻ nhanh"
// Final API may undergo further updates
val quickShareClient = QuickShareClient.create(activity)
val urlAttachment = TextAttachment(
  text = groupAddUrl,
  type = TextAttachment.Type.URL,
)

val content = ShareableContent.withText(urlAttachment)
val appLink = AppLink("https://myapp.com/group/invite")
val request = SharingRequest.withContent(content, appLink)
val session = quickShareClient.startSharingSession(request, lifecycleOwner)
Đoạn mã Kotlin ở phút 30:49 trong video "Tổng quan về tính năng Chia sẻ nhanh"
// Final API may undergo further updates
scope.launch {
  for (event in session.sharingEvents) {
    when (event) {
      is SharingEvent.SendComplete ->
        println("Send finished")
      is SharingEvent.ReceiveComplete ->
        println("Received, files=${event.content?.files?.size}")
      else -> println("other event")
    }
  }
}