1. 事前準備
什麼是工作資料夾?
公司允許員工使用個人裝置工作時,工作資料夾為可在使用者個人裝置上啟用的次要設定檔。
工作資料夾可由 IT 管理員控管,其可用功能則與使用者主要設定檔的功能個別設定。這個方法可讓組織控管公司專用應用程式和資料在使用者裝置上執行的環境,同時讓使用者使用其個人應用程式和設定檔。
這會對應用程式造成什麼影響?任何應用程式都能安裝在工作資料夾底下,代表應用程式可能會受到執行階段限制及行為變更。另外要注意的是,您必須確認應用程式是否能安全處理各種工作事務。即使您的應用程式是在個人資料夾中執行,工作資料夾仍可能會影響您應用程式的行為。
必要條件
本程式碼研究室是為基本到中級 Android 開發人員所設計。
我們假設您具有建構應用程式及使用 Android Studio 的經驗,並且曾在實體裝置或模擬器上測試您的應用程式。
執行步驟
在本程式碼研究室中,我們將引導您修改應用程式,讓使用者能夠順利在設有工作資料夾的裝置上安裝及使用這個應用程式。課程內容包括建構以下功能的應用程式:
- 同時處理公私聯絡人名單。
- 在應用程式中切換工作資料夾和個人資料夾。
軟硬體需求
- 非受管 Android 裝置 (非組織擁有或受組織管理)。
2. 做好準備
設定測試裝置
本程式碼研究室建議使用實體裝置。不過,只要您使用含有 Google Play 商店的映像檔,還是可以透過模擬器進行相同的設定程序。
TestDPC
Google 建構 TestDPC 應用程式,目的是協助您在自己的裝置上模擬及測試受管理的環境。這個應用程式會設定工作資料夾,並提供控制選項,方便您啟用/停用裝置上的特定功能,如同 IT 管理員一樣。
安裝 TestDPC 應用程式
在裝置上開啟 Google Play 商店,然後下載 TestDPC 應用程式
設定工作資料夾
安裝 TestDPC 應用程式後,裝置上就會顯示 2 個圖示:設定圖示和 TestDPC 應用程式圖示。輕觸設定圖示,然後按照畫面上的步驟操作。
現在有兩個不同的設定檔:一個適用於個人應用程式,另一個適用於工作應用程式。您可透過應用程式清單頂部的分頁標籤進行切換。
請注意,每個設定檔都有專用的 Play 商店應用程式。您可以透過啟動器圖示頂部的小型公事包圖片識別工作應用程式。
請照常透過 Play 商店安裝應用程式;視您啟動的 Play 商店而定 (個人與工作),應用程式只會安裝在該設定檔中。從兩 Play 商店安裝應用程式時,應用程式也可存在於這兩個設定檔中。在這種情況下,每個應用程式版本將有完全不同的儲存空間和設定空間。
如何在特定設定檔中安裝應用程式
在以下段落中,我們將探討如何使用 CrossProfileApps 類別切換預設資料夾和工作資料夾。為確認這項行為,應用程式必須同時安裝在兩個設定檔中。
您可以使用下列 ADB 指令,確認資料夾的 ID 編號。
$ adb shell pm list users
您可以使用下列 ADB 指令,將應用程式安裝到指定的資料夾。
$ adb install --user [id number of profile] [path of apk file]
您也可以調整專案的執行/偵錯設定,選取「Install for all users」選項,取得相同結果。
透過 Android Studio 執行應用程式更新時,系統會將應用程式同時安裝在這兩個設定檔中。
3. 載入聯絡人
設定要在範例應用程式中使用的測試聯絡人:
- 從個人資料夾啟動裝置的聯絡人應用程式。
- 新增您可辨別為個人聯絡人的測試聯絡人。
- 從工作資料夾啟動聯絡人應用程式。(您不會看到剛新增的個人聯絡人)。
- 新增您可辨別為工作聯絡人的測試聯絡人。
確認設定的聯絡人後,請嘗試運用範例應用程式的範例程式碼。
4. 取得範例程式碼
- 如要取得範例應用程式,請執行下列任一動作:
- 從 GitHub 複製存放區。
$ git clone https://github.com/android/enterprise-samples.git $ cd enterprise-samples/Work-profile-codelab
- 或透過以下連結下載專案。
- 在 Android Studio 中開啟並執行應用程式。
應用程式首次啟動後,將如下所示:
實際演練
完成本程式碼研究室後,您的應用程式在個人資料夾運作時,會同時顯示公私聯絡人。您也可以在應用程式中啟動另一個設定檔中的應用程式執行個體,藉此切換設定檔。
5. 同時顯示公私聯絡人
使用 ContactsContract.Contacts.CONTENT_URI
載入聯絡人時,應用程式會視其執行的設定檔,決定要顯示的聯絡人。不過,在許多情況下,您可能會想讓應用程式同時載入兩份聯絡人清單。舉例來說,使用者可能想與同事共用個人項目 (相片、文件)。若要這麼做,您必須擷取兩份聯絡人清單。
開啟 MainActivity.kt
onCreateLoader()
方法負責建立游標載入器來擷取和載入聯絡人。目前只會使用預設的 ContentURI 傳回 CursorLoader。您將會呼叫此方法兩次,一次呼叫個人聯絡人,另一次則呼叫工作聯絡人。為了進行區分,我們會分別將不同 ID 傳遞至 onCreateLoader()
。您必須查看傳遞給方法的 ID,以決定要使用的 ContentURI。
首先,根據傳遞給方法的 ID 值,變更 ContentURI 變數的值。若是 PERSONAL_CONTACTS_LOADER_ID,請將其指派給預設的 ContactsContract.Contacts.CONTENT_URI
;如果是其他情況,請按照這個頁面的說明建構 ENTERPRISE_CONTENT_FILTER_URI
。
ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI
.buildUpon()
.appendPath(nameFilter)
.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY,
ContactsContract.Directory.ENTERPRISE_DEFAULT.toString()
)
.build()
您將注意到,由於這是內容篩選器 URI,因此建構工具需要使用搜尋篩選器 (搜尋詞組),才能搜尋/載入聯絡人。
現在,請將搜尋詞組硬式編碼為任何開頭為字母「a」的名稱。
val nameFilter = Uri.encode("a") // names that start with a
您也需要指定聯絡人目錄以進行搜尋。您需要使用 ENTERPRISE_DEFAULT
目錄,才能搜尋儲存在裝置上的聯絡人。
您的 onCreateLoader()
方法應如下所示:
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
val nameFilter = Uri.encode("a") // names that start with a
val contentURI = when (id) {
PERSONAL_CONTACTS_LOADER_ID -> ContactsContract.Contacts.CONTENT_URI
else -> {
ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI
.buildUpon()
.appendPath(nameFilter)
.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY,
ContactsContract.Directory.ENTERPRISE_DEFAULT.toString()
)
.build()
}
}
return CursorLoader(
this, contentURI, arrayOf(
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
), null, null, null
)
}
現在您需要使用新的 ID 值初始化另一個 Loader
,才能觸發上述方法。
首先,請於 MainActivity
頂部建立工作聯絡人的新常數 ID 值:
const val WORK_CONTACTS_LOADER_ID = 1
然後在 initLoaders()
中使用 LoaderManager
,透過上述新建立的 ID 初始化新的 Loader
:
private fun initLoaders() {
LoaderManager.getInstance(this).
initLoader(PERSONAL_CONTACTS_LOADER_ID, null, this)
LoaderManager.getInstance(this).
initLoader(WORK_CONTACTS_LOADER_ID, null, this)
}
由於這兩個載入器的資料游標具有相同結構,因此所有其他方法應該也都適用,運作方式也都相同。
試試看
請在個人資料夾中執行應用程式,現在可以查看公私聯絡人!
工作資料夾會有什麼影響?
如果您是在工作資料夾中執行應用程式,則只會顯示工作聯絡人,且無法查看個人聯絡人。這是因為工作資料夾的主要目標之一是保護使用者的隱私權,因此工作應用程式通常無法存取個人資料夾的任何個人資訊。
6. 在應用程式中切換設定檔
Android 提供的 API 可透過其他設定檔啟動另一個應用程式執行個體,協助使用者切換帳戶。舉例來說,電子郵件應用程式可以提供 UI,讓使用者切換個人資料夾和工作資料夾,以存取兩個電子郵件帳戶。
所有應用程式都能呼叫這些 API,以啟動相同應用程式的主要活動 (如果已安裝於其他設定檔中)。
如要將跨設定檔帳戶切換加入應用程式,請先在主要活動版面配置新增按鈕,以便使用者切換設定檔。
開啟 activity_main.xml
,然後在回收器檢視畫面小工具下方新增按鈕小工具:
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button"
app:layout_constraintTop_toBottomOf="@+id/contacts_rv"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
返回 MainActivity.kt
,在 onCreate
方法中設定按鈕的點擊事件以切換設定檔。
如要這麼做,請先取得 CrossProfileApps
系統服務:
val crossProfileApps = getSystemService(CrossProfileApps::class.java)
這個類別會提供實作設定檔切換功能所需的所有 API。如要擷取使用者設定檔清單,請呼叫 targetUserProfiles
,以傳回已安裝此應用程式的所有其他設定檔。
val userHandles = crossProfileApps.targetUserProfiles
您現在可以使用傳回的第一個 userHandle 項目,在另一個資料夾中啟動應用程式。
crossProfileApps.startMainActivity(
componentName,
userHandles.first()
)
您甚至還能取得提示使用者切換設定檔的本地化文字,並使用該文字設定按鈕的文字值。
val label = crossProfileApps.getProfileSwitchingLabel(userHandles.first())
現在請將這些部分全部合併,然後加入 MainActivity.kt 中 onCreate 方法的尾端:
override fun onCreate(savedInstanceState: Bundle?) {
...
val crossProfileApps = getSystemService(CrossProfileApps::class.java)
val userHandles = crossProfileApps.targetUserProfiles
val label = crossProfileApps.getProfileSwitchingLabel(userHandles.first())
binding.button.apply {
text = label
setOnClickListener {
crossProfileApps.startMainActivity(
componentName,
userHandles.first()
)
}
}
}
試試看
如果您現在執行應用程式,畫面底部會顯示按鈕,指出您可以在「工作資料夾」或「個人資料夾」之間進行切換,視您啟動應用程式的來源而定。
只要按一下這個按鈕,即可在另一個設定檔中啟動應用程式。
7. 恭喜!
您已成功完成修改,讓應用程式能在個人資料夾和工作資料夾中運作,而且即使在個人模式下執行,也能得知是否已安裝工作資料夾,並擷取工作聯絡人。
您也可實作其他方式,讓使用者在執行應用程式時,能夠切換同一個應用程式中的工作及個人資料夾,而無須將其關閉並從適當的設定檔重新啟動。這是可幫助使用者在不同設定檔中以不同方式使用應用程式的好方法。