Improve content of existing managed profile deletion dialog.
The profile deletion confirmation dialog now contains:
* Icon for the MDM managing the profile.
* Name of the MDM.
Bug: 19070402
Change-Id: Ie280d7c5eb8a464be96843f61195c2e2bf5b8ec1
diff --git a/res/layout/delete_managed_profile_dialog.xml b/res/layout/delete_managed_profile_dialog.xml
new file mode 100644
index 0000000..795d3eb
--- /dev/null
+++ b/res/layout/delete_managed_profile_dialog.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (C) 2015 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.
+ */
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/DialogFragmentTextContainer">
+
+ <ScrollView
+ android:layout_weight="1"
+ android:layout_height="0dp"
+ style="@style/ScrollView">
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/delete_managed_profile_opening_paragraph"
+ android:text="@string/opening_paragraph_delete_profile_unknown_company"
+ style="@style/MainText"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/row_height"
+ android:paddingTop="@dimen/row_padding_top"
+ android:layout_alignParentLeft="true"
+ android:layout_centerInParent="true"
+ android:layout_gravity="center_vertical">
+ <ImageView
+ android:id="@+id/delete_managed_profile_mdm_icon_view"
+ android:layout_width="@dimen/icon_width_height"
+ android:layout_height="@dimen/icon_width_height"
+ android:scaleType="center"/>
+ <TextView
+ android:id="@+id/delete_managed_profile_device_manager_name"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/main_text_padding_left_right"
+ android:gravity="center_vertical"
+ android:textColor="@color/text_black"
+ android:textSize="@dimen/main_text_size"/>
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/delete_managed_profile_read_more_link"
+ android:text="@string/read_more_delete_profile"
+ android:visibility="gone"
+ style="@style/MainText"/>
+
+ <TextView
+ android:id="@+id/delete_managed_profile_closing_paragraph"
+ android:text="@string/sure_you_want_to_delete_profile"
+ style="@style/MainText"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/row_height"
+ android:paddingTop="@dimen/row_padding_top">
+
+ <Button
+ android:id="@+id/delete_managed_profile_positive_button"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ android:text="@string/delete_profile"
+ android:textColor="@color/colored_button"
+ style="@style/TransparentButton" />
+
+ <Button
+ android:id="@+id/delete_managed_profile_negative_button"
+ android:layout_alignParentBottom="true"
+ android:layout_toLeftOf="@id/delete_managed_profile_positive_button"
+ android:text="@string/cancel_delete_profile"
+ style="@style/TransparentButton" />
+
+ </RelativeLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b366e19..7bde082 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -53,10 +53,23 @@
<!-- Default profile name used for the creation of work profiles. [CHAR LIMIT=NONE] -->
<string name="default_managed_profile_name">Work profile</string>
+ <!-- Title on the dialog that prompts the user to confirm that they really want to delete their existing work profile-->
+ <string name="delete_profile_title">Remove work profile?</string>
+ <!-- Opening string on the dialog that prompts the user to confirm that they really want to delete
+ their existing work profile. The administration app icon and name appear after the final
+ colon. [CHAR LIMIT=NONE] -->
+ <string name="opening_paragraph_delete_profile_unknown_company">This work profile is managed by:</string>
+ <!-- Opening string on the dialog that prompts the user to confirm that they really want to delete
+ their existing work profile. The administration app icon and name appear after the final
+ colon, the %s is replaced by the domain name of the organization that owns the work
+ profile. [CHAR LIMIT=NONE] -->
+ <string name="opening_paragraph_delete_profile_known_company">This work profile is managed for %s using:</string>
+ <!-- String on the dialog that links through to more information about the profile management application. -->
+ <string name="read_more_delete_profile">Please <a href="#read_this_link">read this</a> before proceeding.</string>
<!-- String on the dialog that prompts the user to confirm that they really want to delete their existing work profile-->
- <string name="sure_you_want_to_delete_profile">You already have a work profile on your device, it must be removed to setup a new profile. All apps and data in this profile will be deleted if you continue.</string>
+ <string name="sure_you_want_to_delete_profile">All apps and data in this profile will be deleted if you continue.</string>
<!-- String on the button that triggers the deletion of a work profile.-->
- <string name="delete_profile">Remove</string>
+ <string name="delete_profile">Delete</string>
<!-- String on the button that cancels out of the deletion of a work profile.-->
<string name="cancel_delete_profile">Cancel</string>
diff --git a/src/com/android/managedprovisioning/DeleteManagedProfileDialog.java b/src/com/android/managedprovisioning/DeleteManagedProfileDialog.java
new file mode 100644
index 0000000..f32787f
--- /dev/null
+++ b/src/com/android/managedprovisioning/DeleteManagedProfileDialog.java
@@ -0,0 +1,142 @@
+package com.android.managedprovisioning;
+/*
+ * Copyright 2015, 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.
+ */
+
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.managedprovisioning.Utils.MdmPackageInfo;
+
+/**
+ * Displays information about an existing managed profile and asks the user if it should be deleted.
+ *
+ * <p>Expects parent component to implement {@link DeleteManagedProfileCallback} for user-response
+ * handling.
+ */
+public class DeleteManagedProfileDialog extends DialogFragment {
+ private static final String KEY_USER_PROFILE_CALLBACK_ID = "user_profile_callback_id";
+ private static final String KEY_MDM_PACKAGE_NAME = "mdm_package_name";
+ private static final String KEY_PROFILE_OWNER_DOMAIN = "profile_owner_domain";
+
+ /**
+ * @param managedProfileUserId user-id for the managed profile which will be passed back to the
+ * parent component in the {@link DeleteManagedProfileCallback#onRemoveProfileApproval(int)}
+ * call
+ * @param mdmPackagename package name of the MDM application for the current managed profile
+ * @param profileOwnerDomain domain name of the organization which owns the managed profile, or
+ * null if not known
+ * @return initialized dialog
+ */
+ public static DeleteManagedProfileDialog newInstance(
+ int managedProfileUserId, ComponentName mdmPackagename, String profileOwnerDomain) {
+ Bundle args = new Bundle();
+ args.putInt(KEY_USER_PROFILE_CALLBACK_ID, managedProfileUserId);
+ args.putString(KEY_MDM_PACKAGE_NAME, mdmPackagename.getPackageName());
+ args.putString(KEY_PROFILE_OWNER_DOMAIN, profileOwnerDomain);
+
+ DeleteManagedProfileDialog dialog = new DeleteManagedProfileDialog();
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ if (!(getActivity() instanceof DeleteManagedProfileCallback)) {
+ throw new IllegalStateException("Calling activity must implement " +
+ "DeleteManagedProfileCallback, found: " + getActivity().getLocalClassName());
+ }
+
+ Bundle arguments = getArguments();
+ final int callbackUserProfileId = arguments.getInt(KEY_USER_PROFILE_CALLBACK_ID);
+ String mdmPackageName = arguments.getString(KEY_MDM_PACKAGE_NAME);
+
+ MdmPackageInfo mdmPackageInfo = Utils.getMdmPackageInfo(
+ getActivity().getPackageManager(), mdmPackageName);
+
+ final Dialog dialog = new Dialog(getActivity(), R.style.ManagedProvisioningDialogTheme);
+ dialog.setTitle(R.string.delete_profile_title);
+ dialog.setContentView(R.layout.delete_managed_profile_dialog);
+ dialog.setCanceledOnTouchOutside(false);
+
+ ImageView imageView = (ImageView) dialog.findViewById(
+ R.id.delete_managed_profile_mdm_icon_view);
+ imageView.setImageDrawable(mdmPackageInfo.getPackageIcon());
+
+ TextView deviceManagerName = (TextView) dialog.findViewById(
+ R.id.delete_managed_profile_device_manager_name);
+ deviceManagerName.setText(mdmPackageInfo.getAppLabel());
+
+ Button positiveButton = (Button) dialog.findViewById(
+ R.id.delete_managed_profile_positive_button);
+ positiveButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dialog.dismiss();
+ ((DeleteManagedProfileCallback) getActivity())
+ .onRemoveProfileApproval(callbackUserProfileId);
+ }
+ });
+
+ Button negativeButton = (Button) dialog.findViewById(
+ R.id.delete_managed_profile_negative_button);
+ negativeButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dialog.dismiss();
+ ((DeleteManagedProfileCallback) getActivity()).onRemoveProfileCancel();
+ }
+ });
+
+ dialog.getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+ return dialog;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ dialog.dismiss();
+ ((DeleteManagedProfileCallback) getActivity()).onRemoveProfileCancel();
+ }
+
+ /**
+ * Callback interface for outcome of {@link DeleteManagedProfileDialog} presentation.
+ */
+ public interface DeleteManagedProfileCallback {
+
+ /**
+ * Invoked if the user hits the positive response (perform removal) button.
+ *
+ * @param managedProfileUserId user-id of the managed-profile that the dialog was presented
+ * for
+ */
+ public abstract void onRemoveProfileApproval(int managedProfileUserId);
+
+ /**
+ * Invoked if the user hits the negative response (DO NOT perform removal) button, or the
+ * dialog was otherwise dismissed.
+ */
+ public abstract void onRemoveProfileCancel();
+ }
+}
diff --git a/src/com/android/managedprovisioning/ProfileOwnerPreProvisioningActivity.java b/src/com/android/managedprovisioning/ProfileOwnerPreProvisioningActivity.java
index efac6e0..c77777c 100644
--- a/src/com/android/managedprovisioning/ProfileOwnerPreProvisioningActivity.java
+++ b/src/com/android/managedprovisioning/ProfileOwnerPreProvisioningActivity.java
@@ -16,27 +16,17 @@
package com.android.managedprovisioning;
-import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
-import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
-import static com.android.managedprovisioning.EncryptDeviceActivity.EXTRA_RESUME;
-import static com.android.managedprovisioning.EncryptDeviceActivity.EXTRA_RESUME_TARGET;
-import static com.android.managedprovisioning.EncryptDeviceActivity.TARGET_PROFILE_OWNER;
-
import android.app.Activity;
-import android.app.admin.DevicePolicyManager;
import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
-import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.PersistableBundle;
@@ -44,26 +34,34 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
-import android.widget.Button;
+import com.android.managedprovisioning.DeleteManagedProfileDialog.DeleteManagedProfileCallback;
+import com.android.managedprovisioning.UserConsentDialog.ConsentCallback;
import com.android.managedprovisioning.Utils.IllegalProvisioningArgumentException;
+import com.android.managedprovisioning.Utils.MdmPackageInfo;
import com.android.setupwizard.navigationbar.SetupWizardNavBar;
import com.android.setupwizard.navigationbar.SetupWizardNavBar.NavigationBarListener;
import java.util.List;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
+import static com.android.managedprovisioning.EncryptDeviceActivity.EXTRA_RESUME;
+import static com.android.managedprovisioning.EncryptDeviceActivity.EXTRA_RESUME_TARGET;
+import static com.android.managedprovisioning.EncryptDeviceActivity.TARGET_PROFILE_OWNER;
+
/**
* The activity sets up the environment in which the {@link ProfileOwnerProvisioningActivity} can be run.
* It makes sure the device is encrypted, the current launcher supports managed profiles, the
* provisioning intent extras are valid, and that the already present managed profile is removed.
*/
public class ProfileOwnerPreProvisioningActivity extends Activity
- implements UserConsentDialog.ConsentCallback, NavigationBarListener {
+ implements ConsentCallback, DeleteManagedProfileCallback, NavigationBarListener {
private static final String MANAGE_USERS_PERMISSION = "android.permission.MANAGE_USERS";
@@ -93,6 +91,8 @@
private Button mSetupButton;
+ private DeleteManagedProfileDialog mDeleteDialog;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -157,11 +157,11 @@
return;
}
- // If there is already a managed profile, allow the user to cancel or delete it.
+ // If there is already a managed profile, setup the profile deletion dialog.
// Otherwise, check whether system has reached maximum user limit.
int existingManagedProfileUserId = alreadyHasManagedProfile();
if (existingManagedProfileUserId != -1) {
- showManagedProfileExistsDialog(existingManagedProfileUserId);
+ createDeleteManagedProfileDialog(dpm, existingManagedProfileUserId);
} else if (isMaximumUserLimitReached()) {
showErrorAndClose(R.string.maximum_user_limit_reached,
"Exiting managed profile provisioning, cannot add more users.");
@@ -170,6 +170,22 @@
}
}
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (alreadyHasManagedProfile() != -1) {
+ showDeleteManagedProfileDialog();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ hideDeleteManagedProfileDialog();
+ }
+
private void showStartProvisioningButton() {
mSetupButton.setVisibility(View.VISIBLE);
}
@@ -217,23 +233,13 @@
}
private void setMdmIcon(String packageName) {
- if (packageName != null) {
- PackageManager pm = getPackageManager();
- try {
- ApplicationInfo ai = pm.getApplicationInfo(packageName, /* default flags */ 0);
- if (ai != null) {
- Drawable packageIcon = pm.getApplicationIcon(packageName);
- ImageView imageView = (ImageView) findViewById(R.id.mdm_icon_view);
- imageView.setImageDrawable(packageIcon);
+ MdmPackageInfo packageInfo = Utils.getMdmPackageInfo(getPackageManager(), packageName);
+ if (packageInfo != null) {
+ ImageView imageView = (ImageView) findViewById(R.id.mdm_icon_view);
+ imageView.setImageDrawable(packageInfo.getPackageIcon());
- String appLabel = pm.getApplicationLabel(ai).toString();
- TextView deviceManagerName = (TextView) findViewById(R.id.device_manager_name);
- deviceManagerName.setText(appLabel);
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Package does not exist, ignore. Should never happen.
- ProvisionLogger.loge("Package does not exist. Should never happen.");
- }
+ TextView deviceManagerName = (TextView) findViewById(R.id.device_manager_name);
+ deviceManagerName.setText(packageInfo.getAppLabel());
}
}
@@ -292,6 +298,19 @@
// Do nothing.
}
+ @Override
+ public void onRemoveProfileApproval(int existingManagedProfileUserId) {
+ mDeleteDialog = null;
+ UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ userManager.removeUser(existingManagedProfileUserId);
+ showStartProvisioningButton();
+ }
+
+ @Override
+ public void onRemoveProfileCancel() {
+ finish();
+ }
+
private void setupEnvironmentAndProvision() {
// Remove any pre-provisioning UI in favour of progress display
BootReminder.cancelProvisioningReminder(this);
@@ -410,35 +429,36 @@
/**
* Builds a dialog that allows the user to remove an existing managed profile.
*/
- private void showManagedProfileExistsDialog(
- final int existingManagedProfileUserId) {
+ private void createDeleteManagedProfileDialog(DevicePolicyManager dpm,
+ int existingManagedProfileUserId) {
+ if (mDeleteDialog != null) {
+ return;
+ }
- DialogInterface.OnClickListener deleteListener =
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- UserManager userManager =
- (UserManager) getSystemService(Context.USER_SERVICE);
- userManager.removeUser(existingManagedProfileUserId);
- showStartProvisioningButton();
- }
- };
+ ComponentName mdmPackageName = dpm.getProfileOwnerAsUser(existingManagedProfileUserId);
+ String domainName = dpm.getProfileOwnerNameAsUser(existingManagedProfileUserId);
- DialogInterface.OnClickListener cancelListener =
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- ProfileOwnerPreProvisioningActivity.this.finish();
- }
- };
+ mDeleteDialog = DeleteManagedProfileDialog.newInstance(existingManagedProfileUserId,
+ mdmPackageName, domainName);
+ }
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setMessage(getString(R.string.sure_you_want_to_delete_profile))
- .setCancelable(false)
- .setPositiveButton(getString(R.string.delete_profile), deleteListener)
- .setNegativeButton(getString(R.string.cancel_delete_profile), cancelListener)
- .show()
- .getWindow().getDecorView().setSystemUiVisibility(IMMERSIVE_FLAGS);
+ private void showDeleteManagedProfileDialog() {
+ if (mDeleteDialog == null) {
+ return;
+ }
+
+ if (!mDeleteDialog.isVisible()) {
+ mDeleteDialog.show(getFragmentManager(), "DeleteManagedProfileDialogFragment");
+ }
+ }
+
+ private void hideDeleteManagedProfileDialog() {
+ if (mDeleteDialog == null) {
+ return;
+ }
+
+ mDeleteDialog.dismiss();
+ mDeleteDialog = null;
}
@Override
diff --git a/src/com/android/managedprovisioning/Utils.java b/src/com/android/managedprovisioning/Utils.java
index bd05bc5..f69d64d 100644
--- a/src/com/android/managedprovisioning/Utils.java
+++ b/src/com/android/managedprovisioning/Utils.java
@@ -16,30 +16,33 @@
package com.android.managedprovisioning;
-import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
-import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
-
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.ComponentName;
-import android.content.Intent;
+import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
-import java.util.List;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
+
/**
* Class containing various auxiliary methods.
*/
public class Utils {
+ private Utils() {}
+
public static Set<String> getCurrentSystemApps(int userId) {
IPackageManager ipm = IPackageManager.Stub.asInterface(ServiceManager
.getService("package"));
@@ -174,4 +177,42 @@
}
return mdmComponentName;
}
+
+ public static MdmPackageInfo getMdmPackageInfo(PackageManager pm, String packageName) {
+ if (packageName != null) {
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(packageName, /* default flags */ 0);
+ if (ai != null) {
+ return new MdmPackageInfo(pm.getApplicationIcon(packageName),
+ pm.getApplicationLabel(ai).toString());
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Package does not exist, ignore. Should never happen.
+ ProvisionLogger.loge("Package does not exist. Should never happen.");
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Information relating to the currently installed MDM package manager.
+ */
+ public static final class MdmPackageInfo {
+ private final Drawable packageIcon;
+ private final String appLabel;
+
+ private MdmPackageInfo(Drawable packageIcon, String appLabel) {
+ this.packageIcon = packageIcon;
+ this.appLabel = appLabel;
+ }
+
+ public String getAppLabel() {
+ return appLabel;
+ }
+
+ public Drawable getPackageIcon() {
+ return packageIcon;
+ }
+ }
}