Xây dựng một ứng dụng chạy trên hồ sơ công việc

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

Hồ sơ công việc là gì?

Hồ sơ công việc là một hồ sơ phụ có thể được kích hoạt trên thiết bị cá nhân của người dùng, khi công ty cho phép nhân viên sử dụng thiết bị cá nhân của họ để làm việc.

Hồ sơ công việc có thể do một quản trị viên CNTT kiểm soát và chức năng có sẵn trong hồ sơ này được cài đặt riêng rẽ với chức năng của hồ sơ chính của người dùng. Phương pháp này cho phép tổ chức kiểm soát môi trường nơi ứng dụng và dữ liệu cụ thể của công ty đang chạy trên thiết bị của người dùng, trong khi vẫn cho phép người dùng sử dụng hồ sơ và ứng dụng cá nhân của họ.

Điều đó ảnh hưởng đến ứng dụng của bạn như thế nào? Bạn có thể cài đặt bất kỳ ứng dụng nào trong hồ sơ công việc, nghĩa là ứng dụng có thể gặp phải hạn chế về thời gian chạy và các thay đổi về hành vi. Cũng cần đảm bảo ứng dụng của bạn được bảo mật nếu ứng dụng này được dùng vì mục đích công việc. Ngay cả khi ứng dụng đang chạy trong hồ sơ cá nhân, hồ sơ công việc vẫn có thể tác động đến cách ứng dụng của bạn hoạt động.

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

Lớp học lập trình này dành cho các nhà phát triển Android có kỹ năng từ cơ bản đến trung cấp.

Giả sử trước đó bạn đã xây dựng một ứng dụng và từng sử dụng Android Studio cũng như kiểm thử ứng dụng đó trên một thiết bị hoặc trình mô phỏng.

Bạn sẽ thực hiện

Trong lớp học lập trình này, bạn sẽ sửa đổi một ứng dụng để mang lại trải nghiệm tốt nhất cho người dùng khi hồ sơ công việc được cài đặt trên thiết bị. Bạn sẽ tìm hiểu cách tạo ứng dụng của mình:

  • Xử lý cùng lúc danh bạ cá nhân và công việc.
  • Chuyển đổi giữa hồ sơ công việc và cá nhân từ bên trong ứng dụng.

caf809dbd1e16c75.png

Bạn cần có

  • Một thiết bị Android không được quản lý (không của tổ chức hoặc được tổ chức quản lý).

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

Thiết lập thiết bị kiểm thử

Bạn nên dùng một thiết bị thực tế (physical device) cho lớp học lập trình này. Tuy nhiên, bạn vẫn có thể thực hiện cùng thiết lập bên dưới trên một trình mô phỏng bằng cách sử dụng một hình ảnh có Cửa hàng Google Play.

TestDPC

Google xây dựng ứng dụng TestDPC để giúp bạn mô phỏng và kiểm thử một môi trường được quản lý trên thiết bị của riêng bạn. Việc này sẽ thiết lập hồ sơ công việc và phép bạn kiểm soát bật/tắt một số tính năng trên thiết bị, giống như quản trị viên CNTT có thể làm.

Cài đặt ứng dụng TestDPC

Trên thiết bị của bạn, mở Cửa hàng Google Play và tải ứng dụng TestDPC xuống

Thiết lập hồ sơ công việc.

Sau khi cài đặt ứng dụng TestDPC, bạn sẽ thấy 2 biểu tượng xuất hiện trên thiết bị, biểu tượng thiết lập và biểu tượng ứng dụng TestDPC. Nhấn vào biểu tượng thiết lập rồi làm theo các bước.

Giờ đây, bạn có hai hồ sơ riêng biệt, một dành cho ứng dụng cá nhân và một dành cho ứng dụng công việc. Bạn có thể chuyển đổi giữa hai hồ sơ này qua các thẻ ở đầu danh sách ứng dụng.

Lưu ý rằng mỗi hồ sơ đều có ứng dụng Cửa hàng Play riêng. Bạn có thể nhận diện các ứng dụng công việc nhờ hình ảnh "cái cặp" nhỏ trên biểu tượng trình chạy.

46175af7ad32979d.gif

Bạn có thể cài đặt ứng dụng thông qua Cửa hàng Play như bình thường. Ngoài ra, tuỳ thuộc vào Cửa hàng Play mà bạn mở (cá nhân hay công việc), ứng dụng sẽ chỉ được cài đặt trong hồ sơ tương ứng. Ứng dụng cũng có thể tồn tại trong cả hai hồ sơ khi được cài đặt từ cả hai Cửa hàng Play. Trong trường hợp đó, mỗi phiên bản ứng dụng sẽ có không gian cấu hình và không gian lưu trữ hoàn toàn riêng biệt.

Cách cài đặt ứng dụng trên hồ sơ cụ thể

Trong các đoạn sau, chúng ta sẽ kiểm tra cách chuyển đổi giữa hồ sơ mặc định và hồ sơ công việc với lớp CrossProfileApps. Để xác nhận hành vi đó, bạn cần cài đặt ứng dụng trong cả 2 hồ sơ

Bạn có thể xác nhận số nhận dạng của hồ sơ bằng lệnh adb dưới đây.

$ adb shell pm list users

Bạn có thể cài đặt ứng dụng vào hồ sơ được chỉ định bằng lệnh adb dưới đây.

$ adb install --user [id number of profile] [path of apk file]

Bạn có thể nhận được các kết quả tương tự, hãy cài đặt Run/Debug Configuration (Cấu hình chạy/gỡ lỗi) trong dự án của bạn sau khi chọn lựa chọn "Install for all users" (Cài đặt cho mọi người dùng).

7634e3dcb0a744ca.png

Khi cập nhật ứng dụng bằng cách chạy ứng dụng trên Android Studio, ứng dụng sẽ được cài đặt trong cả hai hồ sơ.

3. Tải danh bạ

Thiết lập một vài thông tin liên hệ thử nghiệm để dùng trong ứng dụng minh hoạ:

  1. Chạy ứng dụng Danh bạ trên thiết bị từ hồ sơ cá nhân.
  2. Thêm một số thông tin liên hệ thử nghiệm được bạn xác định là liên hệ cá nhân.
  3. Chạy ứng dụng danh bạ từ hồ sơ công việc. (Bạn sẽ không thấy bất kỳ thông tin liên hệ cá nhân nào vừa được thêm vào.)
  4. Thêm một số thông tin hệ thử nghiệm được bạn xác định là thông tin liên hệ công việc.

Khi đã hài lòng với những danh bạ mà bạn thiết lập, hãy thử chạy mã khởi đầu của ứng dụng minh hoạ.

4. Lấy mã khởi đầu

  1. Để lấy ứng dụng mẫu, thực hiện một trong hai cách sau:
  • sao chép kho lưu trữ trên GitHub,
$ git clone https://github.com/android/enterprise-samples.git

$ cd enterprise-samples/Work-profile-codelab
  • Hoặc bạn có thể tải dự án xuống qua đường liên kết dưới đây.

Tải tệp Zip xuống

  1. Mở và chạy ứng dụng trong Android Studio.

Đây là giao diện của ứng dụng khi bạn khởi chạy lần đầu tiên:

f9779ab476511718.png

Chạy thử ứng dụng

Vào cuối lớp học lập trình này, ứng dụng của bạn sẽ hiển thị cả danh bạ công việc lẫn cá nhân khi chạy trong hồ sơ cá nhân. Bạn cũng có thể chuyển đổi giữa hai hồ sơ bằng cách chạy một thực thể khác của ứng dụng trong hồ sơ khác ngay từ bên trong chính ứng dụng.

5. Hiển thị cả danh bạ cá nhân lẫn công việc

Khi tải danh bạ bằng ContactsContract.Contacts.CONTENT_URI, ứng dụng sẽ quyết định hiển thị danh bạ nào tuỳ thuộc vào hồ sơ mà ứng dụng đang chạy. Tuy nhiên, trong nhiều trường hợp, bạn có thể muốn ứng dụng tải cả hai danh bạ cùng một lúc. Ví dụ: người dùng của bạn có thể muốn chia sẻ một mục cá nhân (ảnh, tài liệu) với đồng nghiệp. Để làm như vậy, bạn cần truy xuất cả hai danh bạ.

Mở MainActivity.kt

Phương thức onCreateLoader() chịu trách nhiệm tạo trình tải con trỏ để truy xuất và tải danh bạ. Hiện tại, phương thức này chỉ trả về một CursorLoader bằng ContentURI mặc định. Bạn sẽ gọi phương thức này hai lần, một lần cho danh bạ cá nhân và một lần cho danh bạ công việc. Để phân biệt các danh bạ này, chúng ta sẽ truyền một mã nhận dạng khác nhau vào onCreateLoader() cho từng trường hợp. Bạn cần kiểm tra mã nhận dạng được truyền vào phương thức để quyết định sẽ sử dụng ContentURI nào.

Trước tiên, hãy thay đổi giá trị của biến ContentURI tuỳ theo giá trị của mã nhận dạng được truyền vào phương thức. Trong trường hợp của PERSONAL_CONTACTS_LOADER_ID, gán phương thức này vào ContactsContract.Contacts.CONTENT_URI mặc định, nếu không, bạn sẽ tạo ENTERPRISE_CONTENT_FILTER_URI như được mô tả tại đây.

ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI
                    .buildUpon()
                    .appendPath(nameFilter)
                    .appendQueryParameter(
                        ContactsContract.DIRECTORY_PARAM_KEY,
                        ContactsContract.Directory.ENTERPRISE_DEFAULT.toString()
                    )
                    .build()

Bạn sẽ nhận thấy rằng do đây là URI Bộ lọc nội dung, nên trình tạo cần một bộ lọc tìm kiếm (cụm từ tìm kiếm) để sử dụng khi tìm kiếm/tải danh bạ.

Hiện tại, cố định giá trị trong mã để cụm từ tìm kiếm là bất kỳ tên nào bắt đầu bằng chữ cái "a".

val nameFilter = Uri.encode("a") // names that start with a

Bạn cũng cần phải chỉ định thư mục danh bạ để tìm kiếm. Bạn sẽ dùng thư mục ENTERPRISE_DEFAULT, giúp tìm kiếm thông tin liên hệ được lưu trữ trên thiết bị.

Phương thức onCreateLoader() của bạn sẽ có dạng như sau:

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
        )
    }

Bây giờ, bạn cần khởi chạy một Loader khác, với giá trị mã nhận dạng mới, để kích hoạt phương thức trên.

Trước tiên, tạo một giá trị mã nhận dạng không đổi mới cho danh bạ làm việc ở đầu MainActivity:

const val WORK_CONTACTS_LOADER_ID = 1

Rồi trong initLoaders(), sử dụng LoaderManager để khởi chạy một Loader mới với mã nhận dạng mới được tạo ở trên:

private fun initLoaders() {
        LoaderManager.getInstance(this).
            initLoader(PERSONAL_CONTACTS_LOADER_ID, null, this)
        LoaderManager.getInstance(this).
            initLoader(WORK_CONTACTS_LOADER_ID, null, this)
    }

Tất cả các phương thức khác đều hoạt động theo cùng một cách vì Con trỏ dữ liệu từ cả hai Trình tải đều có cùng cấu trúc.

Chạy thử

Chạy ứng dụng trong hồ sơ cá nhân và giờ đây bạn sẽ thấy cả danh bạ công việc lẫn cá nhân!

3b8f9c73feee88fb.png

Hồ sơ công việc thì sao?

Nếu chạy ứng dụng trong hồ sơ công việc, bạn vẫn chỉ thấy thông tin liên hệ công việc mà không thấy thông tin liên hệ cá nhân nào cả. Đó là vì một trong những mục tiêu chính của hồ sơ công việc là bảo vệ quyền riêng tư của người dùng. Do vậy, các ứng dụng công việc thường không thể truy cập vào bất kỳ thông tin cá nhân nào từ hồ sơ cá nhân.

9312158a2dc03891.png

6. Chuyển đổi hồ sơ bên trong một ứng dụng

Android bao gồm các API để khởi chạy một thực thể khác của ứng dụng trong một hồ sơ khác, giúp người dùng chuyển đổi giữa các tài khoản. Ví dụ: một ứng dụng email có thể cung cấp giao diện người dùng cho phép người dùng chuyển đổi giữa hồ sơ cá nhân và hồ sơ công việc để truy cập vào hai tài khoản email.

Tất cả các ứng dụng đều có thể gọi các API này để chạy hoạt động chính của cùng một ứng dụng nếu ứng dụng đó đã được cài đặt trong hồ sơ khác.

Để thêm nút chuyển đổi tài khoản trong hồ sơ vào ứng dụng của bạn, trước tiên chúng ta cần thêm một nút vào bố cục hoạt động chính, cho phép người dùng chuyển đổi giữa các hồ sơ.

Mở activity_main.xml và thêm một tiện ích nút bên dưới tiện ích thành phần hiển thị tái sinh:

<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"
        />

Quay lại MainActivity.kt, đặt sự kiện nhấp của nút để chuyển đổi hồ sơ trong phương thức onCreate.

Để thực hiện điều này, trước tiên lấy dịch vụ hệ thống CrossProfileApps:

val crossProfileApps = getSystemService(CrossProfileApps::class.java)

Lớp này sẽ cung cấp mọi API cần thiết để triển khai tính năng chuyển đổi hồ sơ. Bạn có thể truy xuất danh sách hồ sơ người dùng bằng cách gọi targetUserProfiles. Thao tác này sẽ trả về tất cả các hồ sơ khác trong đó ứng dụng này được cài đặt.

val userHandles = crossProfileApps.targetUserProfiles

Giờ đây, bạn có thể sử dụng mục đầu tiên userHandle được trả về và khởi chạy ứng dụng trong hồ sơ khác.

crossProfileApps.startMainActivity(
                    componentName,
                    userHandles.first()
                )

Thậm chí, bạn có thể tải văn bản đã bản địa hoá để nhắc người dùng chuyển đổi giữa các hồ sơ và sử dụng văn bản đó để đặt giá trị văn bản cho nút.

val label = crossProfileApps.getProfileSwitchingLabel(userHandles.first())

Lúc này, để gộp tất cả các phần này lại với nhau, hãy thêm mã sau đây ở cuối phương thức onCreate tại MainActivity.kt:

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()
                )
            }
        }
}

Chạy thử

Nếu chạy ứng dụng vào lúc này, bạn sẽ thấy nút ở dưới cùng cho biết ứng dụng đã sẵn sàng để chuyển đổi giữa Hồ sơ công việc hoặc Hồ sơ cá nhân, tuỳ thuộc vào nơi bạn khởi chạy ứng dụng.

Nhấp vào nút đó sẽ khởi chạy ứng dụng trong hồ sơ khác.

db741d4872052fbc.gif

7. Xin chúc mừng!

Bạn đã sửa đổi thành công ứng dụng hoạt động trong cả hồ sơ cá nhân lẫn hồ sơ công việc và nhận biết khi hồ sơ công việc được cài đặt, và truy xuất được danh bạ công việc ngay cả khi chạy trong chế độ cá nhân.

Bạn cũng đã triển khai một cách để người dùng chuyển đổi giữa hồ sơ công việc và hồ sơ cá nhân của cùng một ứng dụng trong khi chạy ứng dụng đó mà không phải đóng ứng dụng lại rồi khởi chạy lại ứng dụng từ hồ sơ thích hợp. Đây là một phương pháp hay để giúp người dùng chạy ứng dụng của bạn một cách khác nhau trong các hồ sơ khác nhau.

Tìm hiểu thêm