1. 簡介
我們幾乎到哪都隨身攜帶手機,但應用程式一直難以根據使用者不斷變化的環境和活動調整體驗。
過去,開發人員必須花費寶貴的工程時間,結合各種信號 (位置、感應器等),才能判斷步行或開車等活動的開始或結束時間。更糟的是,如果應用程式持續獨立檢查使用者活動的變化,電池續航力就會受到影響。
Activity Recognition Transition API 提供簡單的 API,可為您處理所有作業,並只回報您真正關心的資訊:使用者活動的變化。應用程式只要訂閱您感興趣的活動轉換事件,API 就會通知您相關變更
舉例來說,訊息應用程式可以要求「在使用者進入或離開車輛時通知我」,以便將使用者的狀態設為忙碌。同樣地,停車偵測應用程式可以詢問「使用者下車並開始步行時通知我」,以便儲存使用者的停車位置。
在本程式碼研究室中,您將瞭解如何使用 Activity Recognition Transition API,判斷使用者何時開始/停止步行或跑步等活動。
必要條件
熟悉 Android 開發,並對回呼有一定瞭解。
課程內容
- 註冊活動轉換
- 處理這些事件
- 不再需要活動轉換時,取消註冊
軟硬體需求
- Android Studio Bumblebee
- Android 裝置或模擬器
2. 開始使用
複製範例專案存放區
為了方便您盡快上手,我們準備了新手範例專案。如果您已安裝 Git,只要執行下列指令即可。(如要檢查,請在終端機 / 指令列中輸入 git --version,然後確認是否正常運作)。
git clone https://github.com/android/codelab-activity_transitionapi
如果您沒有 Git,可以將專案下載為 ZIP 檔案:
匯入專案
啟動 Android Studio,然後在歡迎畫面中選取「Open an existing Android Studio project」,然後開啟專案目錄。
專案載入後,系統也可能會顯示快訊,通知您 Git 不會追蹤所有本機變更,您可以按一下「Ignore」或右上方的「X」圖示 (系統不會將任何變更推送回 Git 存放區。)
如果您在「Android」檢視畫面中,專案視窗的左上角會顯示如下圖所示的項目 (在「Project」檢視畫面中,您必須展開專案才能看到這些項目)。

共有兩個資料夾圖示 (base 和 complete)。每個資料夾圖示都稱為「module」(模組)。
請注意,第一次開啟專案時,Android Studio 可能需要花幾秒的時間在背景中編譯專案。此時,Android Studio 底部的狀態列會顯示旋轉圖示:

建議您等到此動作完成後再變更程式碼。這使 Android Studio 提取所有必要的元件。
此外,如果畫面顯示「Reload for language changes to take effect?」的提示或類似內容,請選取「Yes」。
瞭解範例專案
一切準備就緒,可以新增活動辨識功能了。我們將使用 base 模組,這是本程式碼研究室的起點。換句話說,您必須在每個步驟中新增程式碼至 base。
complete 模組可用於檢查你的作品,在發生問題時也可以參考。
主要元件總覽:
MainActivity:包含活動辨識所需的所有程式碼。
模擬器設定
如需設定 Android 模擬器的相關說明,請參閱「執行應用程式」一文。
執行範例專案
執行應用程式。
- 將 Android 裝置連接到電腦,或啟動模擬器。
- 在工具列中,從下拉式選取器中選取
base設定,然後按一下旁邊的綠色三角形 (執行) 按鈕:

- 您應該會看到下列應用程式:

- 這個應用程式目前只會列印訊息,不會執行任何其他動作。現在要新增活動辨識功能。
摘要
您在此步驟中瞭解到:
- 程式碼研究室的一般設定。
- 應用程式基本概念。
- 如何部署應用程式。
3. 查看程式庫及在資訊清單中新增權限
如要在應用程式中使用 Transition API,必須宣告 Google Location 及 Activity Recognition API 的依附元件,並在應用程式資訊清單中指定 com.google.android.gms.permission.ACTIVITY_RECOGNITION 權限。
- 在 build.gradle 檔案中,搜尋 「TODO: Review play services library required for activity recognition」(TODO:查看活動辨識所需的 Play 服務程式庫)。這個步驟 (步驟 1) 沒有任何動作,您只需查看我們需要的已宣告依附元件,看起來應該像這樣:
// TODO: Review play services library required for activity recognition.
implementation 'com.google.android.gms:play-services-location:19.0.1'
- 在
base模組中,搜尋AndroidManifest.xml中的「TODO: Add both activity recognition permissions to the manifest」,並將下列程式碼新增至<manifest>元素。
<!-- TODO: Add both activity recognition permissions to the manifest. -->
<!-- Required for 28 and below. -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
您的程式碼現在看起來應該會像這樣:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<!-- TODO: Add both activity recognition permissions to the manifest. -->
<!-- Required for 28 and below. -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
...
</manifest>
如註解所示,您必須為 Android 10 新增第二個權限。這是 API 29 版本新增的執行階段權限。
大功告成!您的應用程式現在可以支援活動辨識功能,只需要新增該程式碼就能完成。
執行應用程式
透過 Android Studio 執行應用程式。看起來應該完全相同。我們尚未實際新增任何用於追蹤轉場效果的程式碼,不過會在下一個章節提供。
4. 在 Android 中檢查/要求執行階段權限
雖然我們已涵蓋 API 版本 28 以下的權限,但我們需要在 API 版本 29 以上支援執行階段權限:
- 我們會在
MainActivity.java中檢查使用者是否使用 Android 10 (29) 以上版本,如果是,我們會檢查活動辨識權限。 - 如果使用者未授予權限,我們會傳送啟動畫面 (
PermissionRationalActivity.java) 給使用者,說明應用程式需要權限的原因並允許使用者核准。
檢查 Android 版本檢查程式碼
在 base 模組中,搜尋 MainActivity.java 中的「TODO: Review check for devices with Android 10 (29+)」。此時,你會看到這個程式碼片段。
注意:這個部分沒有任何動作。
// TODO: Review check for devices with Android 10 (29+).
private boolean runningQOrLater =
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q;
如前所述,您必須先取得 Android 10 以上版本的執行階段權限 android.permission.ACTIVITY_RECOGNITION 核准,我們會使用這項簡單檢查,判斷是否需要檢查執行階段權限。
視需要查看活動辨識的執行階段權限檢查
在 base 模組中,搜尋 MainActivity.java 中的「TODO: Review permission check for 29+」。此時,你會看到這個程式碼片段。
注意:這個部分沒有任何動作。
// TODO: Review permission check for 29+.
if (runningQOrLater) {
return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACTIVITY_RECOGNITION
);
} else {
return true;
}
我們會使用上一步建立的變數,判斷是否需要檢查執行階段權限。
如果是 Q 以上版本,我們會檢查並傳回執行階段權限的結果。這是名為 activityRecognitionPermissionApproved() 的較大方法的一部分,可讓開發人員透過簡單的呼叫,瞭解是否需要要求權限。
要求執行階段權限,並啟用/停用活動辨識轉換
在 base 模組中,搜尋 MainActivity.java 中的「TODO: Enable/Disable activity tracking and ask for permissions if needed」。在註解後方新增以下程式碼。
// TODO: Enable/Disable activity tracking and ask for permissions if needed.
if (activityRecognitionPermissionApproved()) {
if (activityTrackingEnabled) {
disableActivityTransitions();
} else {
enableActivityTransitions();
}
} else {
// Request permission and start activity for result. If the permission is approved, we
// want to make sure we start activity recognition tracking.
Intent startIntent = new Intent(this, PermissionRationalActivity.class);
startActivityForResult(startIntent, 0);
}
這裡會詢問是否核准活動辨識。如果裝置支援這項功能,且動作辨識功能已啟用,系統就會停用這項功能。否則我們會啟用這項設定。
針對使用者未授予權限的情況,我們會傳送啟動畫面活動給使用者,說明需要取得權限的原因並允許使用者啟用。
查看權限要求程式碼
在 base 模組中,搜尋 PermissionRationalActivity.java 中的「TODO: Review permission request for activity recognition」。此時,你會看到這個程式碼片段。
注意:這個部分沒有任何動作。
// TODO: Review permission request for activity recognition.
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.ACTIVITY_RECOGNITION},
PERMISSION_REQUEST_ACTIVITY_RECOGNITION)
這是活動最重要的部分,也是您要審查的部分。使用者要求權限時,程式碼會觸發權限要求。
此外,PermissionRationalActivity.java 類別會顯示使用者應核准活動辨識權限的原因 (最佳做法)。使用者可以點選「不用了」按鈕或「繼續」按鈕 (這會觸發上述程式碼)。
如要瞭解詳情,歡迎查看該檔案。
5. 註冊/取消註冊活動轉換的接收器
在設定活動辨識程式碼之前,我們要先確保 Activity 可以處理系統引發的轉換動作。
為轉場效果建立 BroadcastReceiver
在 base 模組中,搜尋 MainActivity.java 中的「TODO: Create a BroadcastReceiver to listen for activity transitions」。貼上下方程式碼片段。
// TODO: Create a BroadcastReceiver to listen for activity transitions.
// The receiver listens for the PendingIntent above that is triggered by the system when an
// activity transition occurs.
mTransitionsReceiver = new TransitionsReceiver();
為轉移作業註冊 BroadcastReceiver
在 base 模組中,搜尋 MainActivity.java 中的「TODO: Register a BroadcastReceiver to listen for activity transitions」。(位於 onStart() 中)。貼上下方程式碼片段。
// TODO: Register a BroadcastReceiver to listen for activity transitions.
registerReceiver(mTransitionsReceiver, new IntentFilter(TRANSITIONS_RECEIVER_ACTION));
現在,我們終於在透過 PendingIntent 觸發活動轉換時取得更新通知。
取消註冊 BroadcastReceiver
在 base 模組中,搜尋 MainActivity.java 中的「Unregister activity transition receiver when user leaves the app」(使用者離開應用程式時取消註冊活動轉換接收器)。(位於 onStop() 中)。貼上下列程式碼片段。
// TODO: Unregister activity transition receiver when user leaves the app.
unregisterReceiver(mTransitionsReceiver);
最佳做法是在 Activity 關閉時取消註冊接收器。
6. 設定活動轉換和要求更新
如要開始接收活動轉換的狀態更新,請導入下列程式碼:
- ActivityTransitionRequest 物件,用於指定活動類型以及其轉換。
- PendingIntent 回呼,用於應用程式接收通知。詳情請參閱「使用待處理意圖」一節。
建立要追蹤的 ActivityTransition 清單
如要建立 ActivityTransitionRequest 物件,必須建立 ActivityTransition 物件清單,內容為要追蹤的轉換。ActivityTransition 物件包含下列資料:
- 以 DetectedActivity 類別表示的活動類型。Transition API 支援下列活動:
- 以 ActivityTransition 類別表示的轉換類型。轉場效果類型包括:
在 base 模組中,搜尋 MainActivity.java 中的「TODO: Add activity transitions to track」。在註解後方新增以下程式碼。
// TODO: Add activity transitions to track.
activityTransitionList.add(new ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build());
activityTransitionList.add(new ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build());
activityTransitionList.add(new ActivityTransition.Builder()
.setActivityType(DetectedActivity.STILL)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build());
activityTransitionList.add(new ActivityTransition.Builder()
.setActivityType(DetectedActivity.STILL)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build());
這段程式碼會將要追蹤的轉換新增至先前空白的清單。
建立 PendingIntent
如先前所述,如果想在 ActivityTransitionRequest 發生任何變更時收到通知,我們需要 PendingIntent,因此在設定 ActivityTransitionRequest 之前,我們需要建立 PendingIntent。
在 base 模組中,搜尋 MainActivity.java 中的「TODO: Initialize PendingIntent that will be triggered when a activity transition occurs」。在註解後方新增以下程式碼。
// TODO: Initialize PendingIntent that will be triggered when a activity transition occurs.
Intent intent = new Intent(TRANSITIONS_RECEIVER_ACTION);
mActivityTransitionsPendingIntent =
PendingIntent.getBroadcast(MainActivity.this, 0, intent, 0);
現在我們有了 PendingIntent,可以在發生 ActivityTransition 時觸發。
建立 ActivityTransitionRequest 並要求更新
您可以將 ActivityTransitions 清單傳遞至 ActivityTransitionRequest 類別,建立 ActivityTransitionRequest 物件。
在 base 模組中,搜尋 MainActivity.java 中的「Create request and listen for activity changes」。在註解後方新增以下程式碼。
// TODO: Create request and listen for activity changes.
ActivityTransitionRequest request = new ActivityTransitionRequest(activityTransitionList);
// Register for Transitions Updates.
Task<Void> task =
ActivityRecognition.getClient(this)
.requestActivityTransitionUpdates(request, mActivityTransitionsPendingIntent);
task.addOnSuccessListener(
new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void result) {
activityTrackingEnabled = true;
printToScreen("Transitions Api was successfully registered.");
}
});
task.addOnFailureListener(
new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
printToScreen("Transitions Api could NOT be registered: " + e);
Log.e(TAG, "Transitions Api could NOT be registered: " + e);
}
});
我們來看看程式碼。首先,我們要從活動轉換清單建立 ActivityTransitionRequest。
ActivityTransitionRequest request = new ActivityTransitionRequest(activityTransitionList);
接著,將 ActivityTransitionRequest 執行個體和上一步建立的 PendingIntent 物件傳遞至 requestActivityTransitionUpdates() 方法,註冊活動轉換的狀態更新。requestActivityTransitionUpdates() 方法會傳回 Task 物件,可用來確認成功或失敗,如下一個程式碼區塊所示:
// Register for Transitions Updates.
Task<Void> task =
ActivityRecognition.getClient(this)
.requestActivityTransitionUpdates(request, mActivityTransitionsPendingIntent);
task.addOnSuccessListener(
new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void result) {
activityTrackingEnabled = true;
printToScreen("Transitions Api was successfully registered.");
}
});
task.addOnFailureListener(
new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
printToScreen("Transitions Api could NOT be registered: " + e);
Log.e(TAG, "Transitions Api could NOT be registered: " + e);
}
});
成功註冊活動轉換的最新狀態後,應用程式會在註冊的 PendingIntent 中收到通知。我們也設定了活動追蹤已啟用的變數,以便在使用者再次點選按鈕時,判斷是否要停用/啟用追蹤。
在應用程式關閉時移除更新
應用程式關閉時,請務必移除轉場效果更新。
在 base 模組中,搜尋 MainActivity.java 中的「Stop listening for activity changes」(停止監聽活動變更)。在註解後方新增以下程式碼。
// TODO: Stop listening for activity changes.
ActivityRecognition.getClient(this).removeActivityTransitionUpdates(mActivityTransitionsPendingIntent)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
activityTrackingEnabled = false;
printToScreen("Transitions successfully unregistered.");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
printToScreen("Transitions could not be unregistered: " + e);
Log.e(TAG,"Transitions could not be unregistered: " + e);
}
});
現在,我們需要在應用程式關閉時呼叫包含上述程式碼的方法
在 base 模組中,搜尋 MainActivity.java 中 onPause() 的「TODO: Disable activity transitions when user leaves the app」。在註解後方新增以下程式碼。
// TODO: Disable activity transitions when user leaves the app.
if (activityTrackingEnabled) {
disableActivityTransitions();
}
這樣就完成活動轉換變更的追蹤作業。現在只要處理更新即可。
7. 處理事件
當要求的活動發生轉換,應用程式會收到 Intent 回呼。您可以從 Intent 擷取 ActivityTransitionResult 物件,其中包含 ActivityTransitionEvent 物件的清單。事件會依時間先後順序排列,例如,如果應用程式要求 IN_VEHICLE 活動類型 (位於 ACTIVITY_TRANSITION_ENTER 和 ACTIVITY_TRANSITION_EXIT 轉換),則當使用者開始開車時,就會收到一個 ActivityTransitionEvent 物件,當使用者轉換至其他活動時,則會收到另一個。
讓我們新增程式碼來處理這些事件。
在 base 模組中,搜尋 MainActivity.java 中「TODO: Extract activity transition information from listener」,並在稍早建立的 BroadcastReceiver 的 onReceive() 中,在註解後方新增以下程式碼。
// TODO: Extract activity transition information from listener.
if (ActivityTransitionResult.hasResult(intent)) {
ActivityTransitionResult result = ActivityTransitionResult.extractResult(intent);
for (ActivityTransitionEvent event : result.getTransitionEvents()) {
String info = "Transition: " + toActivityString(event.getActivityType()) +
" (" + toTransitionType(event.getTransitionType()) + ")" + " " +
new SimpleDateFormat("HH:mm:ss", Locale.US).format(new Date());
printToScreen(info);
}
}
這會將資訊轉換為 String,並輸出到畫面上。
這樣就完成了!請嘗試執行應用程式。
重要注意事項:在模擬器上很難重現活動變化,因此建議使用實體裝置。
您應該可以追蹤活動變更。
為獲得最佳結果,請在實體裝置上安裝應用程式,然後四處走動。:)
8. 檢查程式碼
您已建構簡單的應用程式,可追蹤活動轉換並列在畫面上。
歡迎您隨時詳閱完整程式碼,瞭解自己完成的部分,並進一步瞭解程式碼如何搭配運作。