1. 簡介
我們隨身攜帶手機,但到目前為止,應用程式很難根據使用者不斷變化的環境和活動調整使用體驗。
過去要完成這項工作,開發人員必須花費寶貴的工程時間,整合各種信號 (位置、感應器等),才能判斷步行或開車等活動何時開始或結束。更糟的是,如果應用程式獨立且持續檢查使用者活動的變化,電池續航力就會受到影響。
Activity Recognition Transition API 可解決這些問題,因為它提供簡單的 API,可為您處理所有作業,並只告知您真正在意的資訊:使用者活動何時發生變化。應用程式只需訂閱您感興趣的活動轉換事件,API 就會通知您變更
舉例來說,訊息應用程式可以要求「當使用者進入或離開車輛時通知我」,以便將使用者的狀態設為忙碌。同樣地,停車偵測應用程式也可以要求「告訴我使用者何時下車並開始步行」,以便儲存使用者的停車地點。
在本程式碼研究室中,您將瞭解如何使用 Activity Recognition Transition API,判斷使用者何時開始/停止步行或跑步等活動。
必要條件
熟悉 Android 開發作業,並對回呼有一定程度的瞭解。
課程內容
- 註冊活動轉換
- 處理這些事件
- 在不再需要時取消註冊活動轉換
軟硬體需求
- Android Studio Bumblebee
- Android 裝置或模擬器
2. 開始使用
複製範例專案存放區
為了方便您盡快上手,我們準備了新手範例專案。如果您已安裝 Git,只要執行下列指令即可。(如要檢查 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
)。每個資料夾圖示都稱為「模組」。
請注意,由於 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 位置和活動辨識 API 的依附元件,並在應用程式資訊清單中指定 com.google.android.gms.permission.ACTIVITY_RECOGNITION 權限。
- 在 build.gradle 檔案中,搜尋 「TODO:查看活動辨識所需的遊戲服務程式庫」。這個步驟 (步驟 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
類別會顯示使用者應核准活動辨識權限的原因 (最佳做法)。使用者可以點選「No Thanks」按鈕或「Continue」按鈕 (會觸發上述程式碼)。
如要進一步瞭解詳情,歡迎查看檔案。
5. 註冊/取消註冊活動轉換的接收器
在設定活動辨識程式碼之前,我們想確保活動可以處理系統提出的轉換動作。
為轉場效果建立 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 回呼,用於應用程式接收通知。詳情請參閱「使用待處理意圖」。
建立要遵循的 ActivitiyTransitions 清單
如要建立 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
模組中,搜尋 onPause()
中的 MainActivity.java
中的「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
模組中,搜尋先前建立的 BroadcastReceiver 的 onReceive()
中的 MainActivity.java
中的「TODO: Extract activity transition information from listener」。在註解後方新增以下程式碼。
// 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. 查看程式碼
您已建構簡單的應用程式,可追蹤活動轉場效果,並在畫面上列出這些轉場效果。
歡迎您隨時詳閱完整程式碼,瞭解自己完成的部分,並進一步瞭解程式碼如何搭配運作。