UX changes for the new cross profile settings page
Bug: 136249261 Bug: 140728653 Bug: 148594054 Test: make RunSettingsRoboTests ROBOTEST_FILTER=InteractAcrossProfilesControllerTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=InteractAcrossProfilesDetailsTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=InteractAcrossProfilesPreferenceControllerTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=InteractAcrossProfilesSettingsTest Change-Id: I9666b34a03e5c082eed7c4120e70a07017ab7ef0
This commit is contained in:
21
res/drawable/ic_analytics_grey.xml
Normal file
21
res/drawable/ic_analytics_grey.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
~ Copyright (C) 2020 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#757575" android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM9,17L7,17v-5h2v5zM13,17h-2v-3h2v3zM13,12h-2v-2h2v2zM17,17h-2L15,7h2v10z"/>
|
||||
</vector>
|
21
res/drawable/ic_storage_grey.xml
Normal file
21
res/drawable/ic_storage_grey.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
~ Copyright (C) 2020 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#757575" android:pathData="M2,20h20v-4L2,16v4zM4,17h2v2L4,19v-2zM2,4v4h20L22,4L2,4zM6,7L4,7L4,5h2v2zM2,14h20v-4L2,10v4zM4,11h2v2L4,13v-2z"/>
|
||||
</vector>
|
114
res/layout/interact_across_profiles_consent_dialog.xml
Normal file
114
res/layout/interact_across_profiles_consent_dialog.xml
Normal file
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2020 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/interact_across_profiles_dialog_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:id="@+id/interact_across_profiles_dialog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:gravity="start"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interact_across_profiles_consent_dialog_title"
|
||||
android:text="@string/interact_across_profiles_consent_dialog_title"
|
||||
style="@style/CrossProfileConsentDialogTitle"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interact_across_profiles_consent_dialog_summary"
|
||||
android:text="@string/interact_across_profiles_consent_dialog_summary"
|
||||
style="@style/CrossProfileConsentDialogDescription"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/app_data_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="16pt"
|
||||
android:gravity="start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/app_data_icon"
|
||||
android:src="@drawable/ic_analytics_grey"
|
||||
style="@style/CrossProfileConsentDialogIcon"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/app_data_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:gravity="start"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/app_data_title"
|
||||
android:text="@string/interact_across_profiles_consent_dialog_app_data_title"
|
||||
style="@style/CrossProfileConsentDialogSubTitle"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/app_data_summary"
|
||||
android:text="@string/interact_across_profiles_consent_dialog_app_data_summary"
|
||||
style="@style/CrossProfileConsentDialogSubDescription"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/permissions_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/permissions_icon"
|
||||
android:src="@drawable/ic_storage_grey"
|
||||
style="@style/CrossProfileConsentDialogIcon"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/permissions_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start"
|
||||
android:paddingStart="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/permissions_title"
|
||||
android:text="@string/interact_across_profiles_consent_dialog_permissions_title"
|
||||
style="@style/CrossProfileConsentDialogSubTitle"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/permissions_summary"
|
||||
android:text="@string/interact_across_profiles_consent_dialog_permissions_summary"
|
||||
style="@style/CrossProfileConsentDialogSubDescription"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</RelativeLayout>
|
@@ -8422,33 +8422,55 @@
|
||||
|
||||
<!-- Special access > Title for managing the settings where users opt-in to connect a work app
|
||||
to its personal equivalent, allowing cross-profile communication. [CHAR LIMIT=50] -->
|
||||
<string name="interact_across_profiles_title" translatable="false">Connected work and personal apps</string>
|
||||
<string name="interact_across_profiles_title" translatable="false">Connected personal and work apps</string>
|
||||
|
||||
<!-- Special access > Connected work and personal apps > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
|
||||
<!-- Special access > Connected personal and work apps > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_empty_text" translatable="false">No connected apps</string>
|
||||
|
||||
<!-- Special access > Connected work and personal apps > Additional keywords to search for. [CHAR LIMIT=NONE] -->
|
||||
<!-- Special access > Connected personal and work apps > Additional keywords to search for. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_keywords" translatable="false">cross profile connected app apps work and personal</string>
|
||||
|
||||
<!-- Apps > App Details > Advanced section string title. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_app_detail_title" translatable="false">Connected work and personal apps</string>
|
||||
<string name="interact_across_profiles_app_detail_title" translatable="false">Connected personal and work apps</string>
|
||||
|
||||
<!-- Apps > App Details > Connected work and personal apps > Switch title. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_app_detail_switch" translatable="false">Connect these apps</string>
|
||||
<!-- Apps > App Details > Connected personal and work apps > Switch title. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_switch_enabled" translatable="false">Connected</string>
|
||||
|
||||
<!-- Apps > App Details > Connected work and personal apps > Description. [CHAR LIMIT=NONE] -->
|
||||
<!-- Apps > App Details > Connected personal and work apps > Switch title. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_switch_disabled" translatable="false">Connect these apps</string>
|
||||
|
||||
<!-- Apps > App Details > Connected personal and work apps > Description. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_summary_1" translatable="false">Connected apps share permissions and can access each other\u2019s data.</string>
|
||||
|
||||
<!-- Apps > App Details > Connected work and personal apps > Description. [CHAR LIMIT=NONE] -->
|
||||
<!-- Apps > App Details > Connected personal and work apps > Description. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_summary_2" translatable="false">Only connect apps that you trust with your personal data.Your data may be exposed to your IT admin.</string>
|
||||
|
||||
<!-- TODO(b/148594054): Replace calendar with actual app name -->
|
||||
<!-- Apps > App Details > Connected work and personal apps > Consent dialog title. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_consent_dialog_title" translatable="false">Trust work Calendar with your personal data?</string>
|
||||
<!-- Apps > App Details > Connected personal and work apps > Consent dialog title. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_consent_dialog_title" translatable="false">Trust work %1$s with your personal data?</string>
|
||||
|
||||
<!-- TODO(b/148594054): Replace calendar with actual app name -->
|
||||
<!-- Apps > App Details > Connected work and personal apps > Consent dialog description. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_consent_dialog_summary" translatable="false">Calendar may expose your personal data to your IT admin</string>
|
||||
<!-- Apps > App Details > Connected personal and work apps > Consent dialog description. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_consent_dialog_summary" translatable="false">%1$s may expose your personal data to your IT admin.</string>
|
||||
|
||||
<!-- Apps > App Details > Connected personal and work apps > Consent dialog App data title. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_consent_dialog_app_data_title" translatable="false">App data</string>
|
||||
|
||||
<!-- Apps > App Details > Connected personal and work apps > Consent dialog App data description. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_consent_dialog_app_data_summary" translatable="false">It can access data in your personal %1$s app.</string>
|
||||
|
||||
<!-- Apps > App Details > Connected personal and work apps > Consent dialog Permissions title. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_consent_dialog_permissions_title" translatable="false">Permissions</string>
|
||||
|
||||
<!-- Apps > App Details > Connected personal and work apps > Consent dialog Permissions description. [CHAR LIMIT=NONE] -->
|
||||
<string name="interact_across_profiles_consent_dialog_permissions_summary" translatable="false">It can use your personal %1$s app\u2019s permissions, like access to location, storage, or contacts.</string>
|
||||
|
||||
<!-- Summary of preference to manage connected personal and work apps, informing the user that currently no apps are connected -->
|
||||
<string name="interact_across_profiles_number_of_connected_apps_none" translatable="false">No apps connected</string>
|
||||
|
||||
<!-- Summary of preference to manage connected personal and work apps, informing the user how many apps are connected -->
|
||||
<plurals name="interact_across_profiles_number_of_connected_apps" translatable="false">
|
||||
<item quantity="one"><xliff:g id="count">%d</xliff:g> app connected</item>
|
||||
<item quantity="other"><xliff:g id="count">%d</xliff:g> apps connected</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Sound & notification > Advanced section: Title for managing Do Not Disturb access option. [CHAR LIMIT=40] -->
|
||||
<string name="manage_zen_access_title">Do Not Disturb access</string>
|
||||
|
@@ -637,4 +637,49 @@
|
||||
<item name="android:clipChildren">true</item>
|
||||
</style>
|
||||
|
||||
<style name="CrossProfileConsentDialogTitle">
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
<item name="android:fontFamily">google-sans-medium</item>
|
||||
<item name="android:paddingTop">36dp</item>
|
||||
<item name="android:paddingBottom">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="CrossProfileConsentDialogDescription">
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginBottom">36dp</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
<item name="android:fontFamily">sans-serif</item>
|
||||
<item name="android:gravity">start</item>
|
||||
</style>
|
||||
|
||||
<style name="CrossProfileConsentDialogIcon">
|
||||
<item name="android:layout_width">24dp</item>
|
||||
<item name="android:layout_height">24dp</item>
|
||||
<item name="android:antialias">true</item>
|
||||
<item name="android:gravity">start</item>
|
||||
</style>
|
||||
|
||||
<style name="CrossProfileConsentDialogSubTitle">
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
<item name="android:fontFamily">sans-serif</item>
|
||||
<item name="android:gravity">start</item>
|
||||
</style>
|
||||
|
||||
<style name="CrossProfileConsentDialogSubDescription">
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
<item name="android:fontFamily">sans-serif</item>
|
||||
<item name="android:gravity">start</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
@@ -24,15 +24,13 @@
|
||||
android:selectable="false"/>
|
||||
|
||||
<SwitchPreference
|
||||
android:key="interact_across_profiles_settings_switch"
|
||||
android:title="@string/interact_across_profiles_app_detail_switch"/>
|
||||
android:key="interact_across_profiles_settings_switch" />
|
||||
|
||||
<Preference
|
||||
android:summary="@string/interact_across_profiles_summary_1"
|
||||
android:selectable="false"/>
|
||||
android:selectable="false" />
|
||||
|
||||
<Preference
|
||||
android:summary="@string/interact_across_profiles_summary_2"
|
||||
android:selectable="false"/>
|
||||
|
||||
android:selectable="false" />
|
||||
</PreferenceScreen>
|
||||
|
@@ -39,6 +39,13 @@
|
||||
android:value="com.android.settings.Settings$HighPowerApplicationsActivity" />
|
||||
</Preference>
|
||||
|
||||
<Preference
|
||||
android:key="interact_across_profiles"
|
||||
android:title="@string/interact_across_profiles_title"
|
||||
android:fragment="com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesSettings"
|
||||
settings:keywords="@string/interact_across_profiles_keywords"
|
||||
settings:controller="com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesController" />
|
||||
|
||||
<Preference
|
||||
android:key="device_administrators"
|
||||
android:title="@string/manage_device_admin"
|
||||
@@ -146,13 +153,6 @@
|
||||
android:value="com.android.settings.Settings$ChangeWifiStateActivity" />
|
||||
</Preference>
|
||||
|
||||
<Preference
|
||||
android:key="interact_across_profiles"
|
||||
android:title="@string/interact_across_profiles_title"
|
||||
android:fragment="com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesSettings"
|
||||
settings:keywords="@string/interact_across_profiles_keywords"
|
||||
settings:controller="com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesController"/>
|
||||
|
||||
<Preference
|
||||
android:key="special_access_more"
|
||||
android:title="@string/special_access_more"
|
||||
|
@@ -17,10 +17,13 @@
|
||||
package com.android.settings.applications.specialaccess.interactacrossprofiles;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.CrossProfileApps;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import java.util.List;
|
||||
@@ -33,12 +36,16 @@ public class InteractAcrossProfilesController extends BasePreferenceController {
|
||||
|
||||
private final Context mContext;
|
||||
private final UserManager mUserManager;
|
||||
private final PackageManager mPackageManager;
|
||||
private final CrossProfileApps mCrossProfileApps;
|
||||
|
||||
public InteractAcrossProfilesController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
|
||||
mContext = context;
|
||||
mUserManager = mContext.getSystemService(UserManager.class);
|
||||
mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class);
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,4 +58,17 @@ public class InteractAcrossProfilesController extends BasePreferenceController {
|
||||
}
|
||||
return DISABLED_FOR_USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
final int connectedApps = InteractAcrossProfilesSettings.getNumberOfEnabledApps(
|
||||
mContext, mPackageManager, mUserManager, mCrossProfileApps);
|
||||
return connectedApps == 0
|
||||
? mContext.getResources().getString(
|
||||
R.string.interact_across_profiles_number_of_connected_apps_none)
|
||||
: mContext.getResources().getQuantityString(
|
||||
R.plurals.interact_across_profiles_number_of_connected_apps,
|
||||
connectedApps,
|
||||
connectedApps);
|
||||
}
|
||||
}
|
||||
|
@@ -22,13 +22,15 @@ import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.PermissionChecker;
|
||||
import android.content.pm.CrossProfileApps;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
@@ -51,6 +53,7 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
|
||||
private UserManager mUserManager;
|
||||
private SwitchPreference mSwitchPref;
|
||||
private LayoutPreference mHeader;
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -59,6 +62,7 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
|
||||
mContext = getContext();
|
||||
mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class);
|
||||
mUserManager = mContext.getSystemService(UserManager.class);
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
|
||||
addPreferencesFromResource(R.xml.interact_across_profiles_permissions_details);
|
||||
mSwitchPref = findPreference(INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH);
|
||||
@@ -72,10 +76,17 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
|
||||
}
|
||||
final UserHandle workProfile = getWorkProfile();
|
||||
final UserHandle personalProfile = mUserManager.getProfileParent(workProfile);
|
||||
addAppIcons(personalProfile, workProfile);
|
||||
addAppTitleAndIcons(personalProfile, workProfile);
|
||||
}
|
||||
|
||||
private void addAppIcons(UserHandle personalProfile, UserHandle workProfile) {
|
||||
private void addAppTitleAndIcons(UserHandle personalProfile, UserHandle workProfile) {
|
||||
final TextView title = mHeader.findViewById(R.id.entity_header_title);
|
||||
if (title != null) {
|
||||
final String appLabel = mPackageInfo.applicationInfo.loadLabel(
|
||||
mPackageManager).toString();
|
||||
title.setText(appLabel);
|
||||
}
|
||||
|
||||
final ImageView personalIconView = mHeader.findViewById(R.id.entity_header_icon_personal);
|
||||
if (personalIconView != null) {
|
||||
personalIconView.setImageDrawable(IconDrawableFactory.newInstance(mContext)
|
||||
@@ -114,35 +125,57 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
|
||||
return true;
|
||||
}
|
||||
if (!isInteractAcrossProfilesEnabled()) {
|
||||
// TODO(b/148594054): Create a proper dialogue.
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.interact_across_profiles_consent_dialog_title)
|
||||
.setMessage(R.string.interact_across_profiles_consent_dialog_summary)
|
||||
.setPositiveButton(R.string.allow, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
enableInteractAcrossProfiles(true);
|
||||
refreshUi();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.deny, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
refreshUi();
|
||||
}
|
||||
})
|
||||
.create().show();
|
||||
} else {
|
||||
enableInteractAcrossProfiles(false);
|
||||
refreshUi();
|
||||
showConsentDialog();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showConsentDialog() {
|
||||
final String appLabel = mPackageInfo.applicationInfo.loadLabel(mPackageManager).toString();
|
||||
|
||||
final View dialogView = getLayoutInflater().inflate(
|
||||
R.layout.interact_across_profiles_consent_dialog, null);
|
||||
|
||||
final TextView dialogTitle = dialogView.findViewById(
|
||||
R.id.interact_across_profiles_consent_dialog_title);
|
||||
dialogTitle.setText(
|
||||
getString(R.string.interact_across_profiles_consent_dialog_title, appLabel));
|
||||
|
||||
final TextView dialogSummary = dialogView.findViewById(
|
||||
R.id.interact_across_profiles_consent_dialog_summary);
|
||||
dialogSummary.setText(
|
||||
getString(R.string.interact_across_profiles_consent_dialog_summary, appLabel));
|
||||
|
||||
final TextView appDataSummary = dialogView.findViewById(R.id.app_data_summary);
|
||||
appDataSummary.setText(getString(
|
||||
R.string.interact_across_profiles_consent_dialog_app_data_summary, appLabel));
|
||||
|
||||
final TextView permissionsSummary = dialogView.findViewById(R.id.permissions_summary);
|
||||
permissionsSummary.setText(getString(
|
||||
R.string.interact_across_profiles_consent_dialog_permissions_summary, appLabel));
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setView(dialogView)
|
||||
.setPositiveButton(R.string.allow, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
enableInteractAcrossProfiles(true);
|
||||
refreshUi();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.deny, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
refreshUi();
|
||||
}
|
||||
})
|
||||
.create().show();
|
||||
}
|
||||
|
||||
private boolean isInteractAcrossProfilesEnabled() {
|
||||
return isInteractAcrossProfilesEnabled(
|
||||
mContext, mPackageName, mPackageInfo.applicationInfo.uid);
|
||||
}
|
||||
|
||||
private static boolean isInteractAcrossProfilesEnabled(Context context, String packageName, int uid) {
|
||||
static boolean isInteractAcrossProfilesEnabled(Context context, String packageName, int uid) {
|
||||
return PermissionChecker.PERMISSION_GRANTED
|
||||
== PermissionChecker.checkPermissionForPreflight(
|
||||
context,
|
||||
@@ -178,13 +211,21 @@ public class InteractAcrossProfilesDetails extends AppInfoBase
|
||||
return false;
|
||||
}
|
||||
|
||||
mSwitchPref.setChecked(isInteractAcrossProfilesEnabled());
|
||||
final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz);
|
||||
if (horizontalArrowIcon != null) {
|
||||
final Drawable icon = mSwitchPref.isChecked()
|
||||
? mContext.getDrawable(R.drawable.ic_swap_horiz_blue)
|
||||
: mContext.getDrawable(R.drawable.ic_swap_horiz_grey);
|
||||
horizontalArrowIcon.setImageDrawable(icon);
|
||||
if (isInteractAcrossProfilesEnabled()) {
|
||||
mSwitchPref.setChecked(true);
|
||||
mSwitchPref.setTitle(R.string.interact_across_profiles_switch_enabled);
|
||||
if (horizontalArrowIcon != null) {
|
||||
horizontalArrowIcon.setImageDrawable(
|
||||
mContext.getDrawable(R.drawable.ic_swap_horiz_blue));
|
||||
}
|
||||
} else {
|
||||
mSwitchPref.setChecked(false);
|
||||
mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled);
|
||||
if (horizontalArrowIcon != null) {
|
||||
horizontalArrowIcon.setImageDrawable(
|
||||
mContext.getDrawable(R.drawable.ic_swap_horiz_grey));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@@ -73,7 +73,7 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings {
|
||||
screen.removeAll();
|
||||
|
||||
final ArrayList<Pair<ApplicationInfo, UserHandle>> crossProfileApps =
|
||||
collectConfigurableApps();
|
||||
collectConfigurableApps(mPackageManager, mUserManager, mCrossProfileApps);
|
||||
|
||||
final Context prefContext = getPrefContext();
|
||||
for (final Pair<ApplicationInfo, UserHandle> appData : crossProfileApps) {
|
||||
@@ -124,21 +124,37 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings {
|
||||
* @return the list of applications for the personal profile in the calling user's profile group
|
||||
* that can configure interact across profiles.
|
||||
*/
|
||||
ArrayList<Pair<ApplicationInfo, UserHandle>> collectConfigurableApps() {
|
||||
final UserHandle personalProfile = getPersonalProfileForCallingUser();
|
||||
static ArrayList<Pair<ApplicationInfo, UserHandle>> collectConfigurableApps(
|
||||
PackageManager packageManager, UserManager userManager,
|
||||
CrossProfileApps crossProfileApps) {
|
||||
final UserHandle personalProfile = getPersonalProfileForCallingUser(userManager);
|
||||
if (personalProfile == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
final ArrayList<Pair<ApplicationInfo, UserHandle>> crossProfileApps = new ArrayList<>();
|
||||
final List<PackageInfo> installedPackages = mPackageManager.getInstalledPackagesAsUser(
|
||||
final ArrayList<Pair<ApplicationInfo, UserHandle>> apps = new ArrayList<>();
|
||||
final List<PackageInfo> installedPackages = packageManager.getInstalledPackagesAsUser(
|
||||
GET_ACTIVITIES, personalProfile.getIdentifier());
|
||||
for (PackageInfo packageInfo : installedPackages) {
|
||||
if (mCrossProfileApps.canConfigureInteractAcrossProfiles(packageInfo.packageName)) {
|
||||
crossProfileApps.add(new Pair<>(packageInfo.applicationInfo, personalProfile));
|
||||
if (crossProfileApps.canConfigureInteractAcrossProfiles(packageInfo.packageName)) {
|
||||
apps.add(new Pair<>(packageInfo.applicationInfo, personalProfile));
|
||||
}
|
||||
}
|
||||
return crossProfileApps;
|
||||
return apps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of applications that can interact across profiles.
|
||||
*/
|
||||
static int getNumberOfEnabledApps(
|
||||
Context context, PackageManager packageManager, UserManager userManager,
|
||||
CrossProfileApps crossProfileApps) {
|
||||
final ArrayList<Pair<ApplicationInfo, UserHandle>> apps =
|
||||
collectConfigurableApps(packageManager, userManager, crossProfileApps);
|
||||
apps.removeIf(
|
||||
app -> !InteractAcrossProfilesDetails.isInteractAcrossProfilesEnabled(
|
||||
context, app.first.packageName, app.first.uid));
|
||||
return apps.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,12 +162,12 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings {
|
||||
* Returns null if user is not in a profile group.
|
||||
*/
|
||||
@Nullable
|
||||
private UserHandle getPersonalProfileForCallingUser() {
|
||||
private static UserHandle getPersonalProfileForCallingUser(UserManager userManager) {
|
||||
final int callingUser = UserHandle.myUserId();
|
||||
if (mUserManager.getProfiles(callingUser).isEmpty()) {
|
||||
if (userManager.getProfiles(callingUser).isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final UserInfo parentProfile = mUserManager.getProfileParent(callingUser);
|
||||
final UserInfo parentProfile = userManager.getProfileParent(callingUser);
|
||||
return parentProfile == null
|
||||
? UserHandle.of(callingUser) : parentProfile.getUserHandle();
|
||||
}
|
||||
|
@@ -20,10 +20,12 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.CrossProfileApps;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Pair;
|
||||
@@ -32,12 +34,10 @@ import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.shadows.ShadowProcess;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -47,6 +47,7 @@ public class InteractAcrossProfilesSettingsTest {
|
||||
private static final int PERSONAL_PROFILE_ID = 0;
|
||||
private static final int WORK_PROFILE_ID = 10;
|
||||
private static final int WORK_UID = UserHandle.PER_USER_RANGE * WORK_PROFILE_ID;
|
||||
private static final int PACKAGE_UID = 0;
|
||||
|
||||
private static final String PERSONAL_CROSS_PROFILE_PACKAGE = "personalCrossProfilePackage";
|
||||
private static final String PERSONAL_NON_CROSS_PROFILE_PACKAGE =
|
||||
@@ -58,21 +59,17 @@ public class InteractAcrossProfilesSettingsTest {
|
||||
ImmutableList.of(PERSONAL_CROSS_PROFILE_PACKAGE, PERSONAL_NON_CROSS_PROFILE_PACKAGE);
|
||||
private static final List<String> WORK_PROFILE_INSTALLED_PACKAGES =
|
||||
ImmutableList.of(WORK_CROSS_PROFILE_PACKAGE, WORK_NON_CROSS_PROFILE_PACKAGE);
|
||||
public static final String INTERACT_ACROSS_PROFILES_PERMISSION =
|
||||
"android.permission.INTERACT_ACROSS_PROFILES";
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private final PackageManager mPackageManager = mContext.getPackageManager();
|
||||
private final UserManager mUserManager = mContext.getSystemService(UserManager.class);
|
||||
private final CrossProfileApps mCrossProfileApps =
|
||||
mContext.getSystemService(CrossProfileApps.class);
|
||||
private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
|
||||
private final InteractAcrossProfilesSettings mFragment = new InteractAcrossProfilesSettings();
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
ReflectionHelpers.setField(mFragment, "mPackageManager", mPackageManager);
|
||||
ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager);
|
||||
ReflectionHelpers.setField(mFragment, "mCrossProfileApps", mCrossProfileApps);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void collectConfigurableApps_fromPersonal_returnsPersonalPackages() {
|
||||
shadowOf(mUserManager).addUser(
|
||||
@@ -87,7 +84,8 @@ public class InteractAcrossProfilesSettingsTest {
|
||||
shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE);
|
||||
shadowOf(mCrossProfileApps).addCrossProfilePackage(WORK_CROSS_PROFILE_PACKAGE);
|
||||
|
||||
List<Pair<ApplicationInfo, UserHandle>> apps = mFragment.collectConfigurableApps();
|
||||
List<Pair<ApplicationInfo, UserHandle>> apps = mFragment.collectConfigurableApps(
|
||||
mPackageManager, mUserManager, mCrossProfileApps);
|
||||
|
||||
assertThat(apps.size()).isEqualTo(1);
|
||||
assertThat(apps.get(0).first.packageName).isEqualTo(PERSONAL_CROSS_PROFILE_PACKAGE);
|
||||
@@ -108,7 +106,8 @@ public class InteractAcrossProfilesSettingsTest {
|
||||
shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE);
|
||||
shadowOf(mCrossProfileApps).addCrossProfilePackage(WORK_CROSS_PROFILE_PACKAGE);
|
||||
|
||||
List<Pair<ApplicationInfo, UserHandle>> apps = mFragment.collectConfigurableApps();
|
||||
List<Pair<ApplicationInfo, UserHandle>> apps = mFragment.collectConfigurableApps(
|
||||
mPackageManager, mUserManager, mCrossProfileApps);
|
||||
|
||||
assertThat(apps.size()).isEqualTo(1);
|
||||
assertThat(apps.get(0).first.packageName).isEqualTo(PERSONAL_CROSS_PROFILE_PACKAGE);
|
||||
@@ -122,8 +121,40 @@ public class InteractAcrossProfilesSettingsTest {
|
||||
PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES);
|
||||
shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE);
|
||||
|
||||
List<Pair<ApplicationInfo, UserHandle>> apps = mFragment.collectConfigurableApps();
|
||||
List<Pair<ApplicationInfo, UserHandle>> apps = mFragment.collectConfigurableApps(
|
||||
mPackageManager, mUserManager, mCrossProfileApps);
|
||||
|
||||
assertThat(apps).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNumberOfEnabledApps_returnsNumberOfEnabledApps() {
|
||||
shadowOf(mUserManager).addUser(
|
||||
PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */);
|
||||
shadowOf(mUserManager).addProfile(
|
||||
PERSONAL_PROFILE_ID, WORK_PROFILE_ID,
|
||||
"work-profile"/* profileName */, 0/* profileFlags */);
|
||||
shadowOf(mPackageManager).setInstalledPackagesForUserId(
|
||||
PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES);
|
||||
shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE);
|
||||
shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_NON_CROSS_PROFILE_PACKAGE);
|
||||
String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION);
|
||||
shadowOf(mAppOpsManager).setMode(
|
||||
appOp, PACKAGE_UID, PERSONAL_CROSS_PROFILE_PACKAGE, AppOpsManager.MODE_ALLOWED);
|
||||
shadowOf(mAppOpsManager).setMode(
|
||||
appOp, PACKAGE_UID, PERSONAL_CROSS_PROFILE_PACKAGE, AppOpsManager.MODE_IGNORED);
|
||||
shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo());
|
||||
|
||||
int numOfApps = mFragment.getNumberOfEnabledApps(
|
||||
mContext, mPackageManager, mUserManager, mCrossProfileApps);
|
||||
|
||||
assertThat(numOfApps).isEqualTo(1);
|
||||
}
|
||||
|
||||
private PermissionInfo createCrossProfilesPermissionInfo() {
|
||||
PermissionInfo permissionInfo = new PermissionInfo();
|
||||
permissionInfo.name = INTERACT_ACROSS_PROFILES_PERMISSION;
|
||||
permissionInfo.protectionLevel = PermissionInfo.PROTECTION_FLAG_APPOP;
|
||||
return permissionInfo;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user