Merge "Add uninstall option in the capabilities confirm dialog" into sc-dev
diff --git a/res/layout/enable_accessibility_service_dialog_content.xml b/res/layout/enable_accessibility_service_dialog_content.xml
index 59cb6dd..6b3220b 100644
--- a/res/layout/enable_accessibility_service_dialog_content.xml
+++ b/res/layout/enable_accessibility_service_dialog_content.xml
@@ -134,6 +134,11 @@
                 android:text="@string/accessibility_dialog_button_deny"
                 style="@style/AccessibilityDialogButton" />
 
+            <Button
+                android:id="@+id/permission_enable_uninstall_button"
+                android:text="@string/uninstall_text"
+                android:visibility="gone"
+                style="@style/AccessibilityDialogButton" />
         </LinearLayout>
     </LinearLayout>
 
diff --git a/src/com/android/settings/accessibility/AccessibilityServiceWarning.java b/src/com/android/settings/accessibility/AccessibilityServiceWarning.java
index 2206e81..dcf7897 100644
--- a/src/com/android/settings/accessibility/AccessibilityServiceWarning.java
+++ b/src/com/android/settings/accessibility/AccessibilityServiceWarning.java
@@ -35,6 +35,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.appcompat.app.AlertDialog;
 import androidx.core.content.ContextCompat;
 
@@ -60,11 +61,19 @@
         return false;
     };
 
+    /**
+     * The interface to execute the uninstallation action.
+     */
+    interface UninstallActionPerformer {
+        void uninstallPackage();
+    }
+
     /** Returns a {@link Dialog} to be shown to confirm that they want to enable a service. */
-    public static Dialog createCapabilitiesDialog(Context context,
-            AccessibilityServiceInfo info, View.OnClickListener listener) {
+    public static Dialog createCapabilitiesDialog(@NonNull Context context,
+            @NonNull AccessibilityServiceInfo info, @NonNull View.OnClickListener listener,
+            @NonNull UninstallActionPerformer performer) {
         final AlertDialog ad = new AlertDialog.Builder(context)
-                .setView(createEnableDialogContentView(context, info, listener))
+                .setView(createEnableDialogContentView(context, info, listener, performer))
                 .create();
 
         Window window = ad.getWindow();
@@ -88,7 +97,8 @@
     }
 
     private static View createEnableDialogContentView(Context context,
-            AccessibilityServiceInfo info, View.OnClickListener listener) {
+            @NonNull AccessibilityServiceInfo info, View.OnClickListener listener,
+            UninstallActionPerformer performer) {
         LayoutInflater inflater = (LayoutInflater) context.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
 
@@ -129,6 +139,14 @@
         permissionAllowButton.setOnTouchListener(filterTouchListener);
         permissionDenyButton.setOnClickListener(listener);
 
+        final Button uninstallButton = content.findViewById(
+                R.id.permission_enable_uninstall_button);
+        // Shows an uninstall button to help users quickly remove the non-system App due to the
+        // required permissions.
+        if (!AccessibilityUtil.isSystemApp(info)) {
+            uninstallButton.setVisibility(View.VISIBLE);
+            uninstallButton.setOnClickListener(v -> performer.uninstallPackage());
+        }
         return content;
     }
 
diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java
index eb202b5..f547209 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtil.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtil.java
@@ -381,4 +381,13 @@
         return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
                 resources.getDisplayMetrics()));
     }
+
+    /**
+     * Indicates if the accessibility service belongs to a system App.
+     * @param info AccessibilityServiceInfo
+     * @return {@code true} if the App is a system App.
+     */
+    public static boolean isSystemApp(@NonNull AccessibilityServiceInfo info) {
+        return info.getResolveInfo().serviceInfo.applicationInfo.isSystemApp();
+    }
 }
diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
index 02016e5..7902ec6 100644
--- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
@@ -24,10 +24,14 @@
 import android.app.Dialog;
 import android.app.admin.DevicePolicyManager;
 import android.app.settings.SettingsEnums;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
@@ -37,12 +41,14 @@
 import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.Switch;
 
+import androidx.annotation.Nullable;
 import androidx.preference.Preference;
 
 import com.android.internal.widget.LockPatternUtils;
@@ -59,7 +65,8 @@
 public class ToggleAccessibilityServicePreferenceFragment extends
         ToggleFeaturePreferenceFragment {
 
-    public static final int ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION = 1;
+    private static final String TAG = "ToggleAccessibilityServicePreferenceFragment";
+    private static final int ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION = 1;
     private LockPatternUtils mLockPatternUtils;
     private AtomicBoolean mIsDialogShown = new AtomicBoolean(/* initialValue= */ false);
 
@@ -74,6 +81,7 @@
             };
 
     private Dialog mDialog;
+    private BroadcastReceiver mPackageRemovedReceiver;
 
     @Override
     public int getMetricsCategory() {
@@ -94,6 +102,17 @@
     }
 
     @Override
+    public void onStart() {
+        super.onStart();
+        final AccessibilityServiceInfo serviceInfo = getAccessibilityServiceInfo();
+        if (serviceInfo == null) {
+            getActivity().finishAndRemoveTask();
+        } else if (!AccessibilityUtil.isSystemApp(serviceInfo)) {
+            registerPackageRemoveReceiver();
+        }
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
         updateSwitchBarToggleSwitch();
@@ -111,6 +130,7 @@
     // capabilities. For
     // example, before JellyBean MR2 the user was granting the explore by touch
     // one.
+    @Nullable
     AccessibilityServiceInfo getAccessibilityServiceInfo() {
         final List<AccessibilityServiceInfo> infos = AccessibilityManager.getInstance(
                 getPrefContext()).getInstalledAccessibilityServiceList();
@@ -136,7 +156,8 @@
                 }
                 mDialog = AccessibilityServiceWarning
                         .createCapabilitiesDialog(getPrefContext(), info,
-                                this::onDialogButtonFromEnableToggleClicked);
+                                this::onDialogButtonFromEnableToggleClicked,
+                                this::onDialogButtonFromUninstallClicked);
                 break;
             }
             case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: {
@@ -146,7 +167,8 @@
                 }
                 mDialog = AccessibilityServiceWarning
                         .createCapabilitiesDialog(getPrefContext(), info,
-                                this::onDialogButtonFromShortcutToggleClicked);
+                                this::onDialogButtonFromShortcutToggleClicked,
+                                this::onDialogButtonFromUninstallClicked);
                 break;
             }
             case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: {
@@ -156,7 +178,8 @@
                 }
                 mDialog = AccessibilityServiceWarning
                         .createCapabilitiesDialog(getPrefContext(), info,
-                                this::onDialogButtonFromShortcutClicked);
+                                this::onDialogButtonFromShortcutClicked,
+                                this::onDialogButtonFromUninstallClicked);
                 break;
             }
             case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: {
@@ -246,6 +269,32 @@
         }
     }
 
+    private void registerPackageRemoveReceiver() {
+        if (mPackageRemovedReceiver != null || getContext() == null) {
+            return;
+        }
+        mPackageRemovedReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String packageName = intent.getData().getSchemeSpecificPart();
+                if (TextUtils.equals(mComponentName.getPackageName(), packageName)) {
+                    getActivity().finishAndRemoveTask();
+                }
+            }
+        };
+        final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        getContext().registerReceiver(mPackageRemovedReceiver, filter);
+    }
+
+    private void unregisterPackageRemoveReceiver() {
+        if (mPackageRemovedReceiver == null || getContext() == null) {
+            return;
+        }
+        getContext().unregisterReceiver(mPackageRemovedReceiver);
+        mPackageRemovedReceiver = null;
+    }
+
     private boolean isServiceSupportAccessibilityButton() {
         final AccessibilityManager ams = getPrefContext().getSystemService(
                 AccessibilityManager.class);
@@ -378,6 +427,35 @@
         }
     }
 
+    private void onDialogButtonFromUninstallClicked() {
+        mDialog.dismiss();
+        final Intent uninstallIntent = createUninstallPackageActivityIntent();
+        if (uninstallIntent == null) {
+            return;
+        }
+        startActivity(uninstallIntent);
+    }
+
+    @Nullable
+    private Intent createUninstallPackageActivityIntent() {
+        final AccessibilityServiceInfo a11yServiceInfo = getAccessibilityServiceInfo();
+        if (a11yServiceInfo == null) {
+            Log.w(TAG, "createUnInstallIntent -- invalid a11yServiceInfo");
+            return null;
+        }
+        final ApplicationInfo appInfo =
+                a11yServiceInfo.getResolveInfo().serviceInfo.applicationInfo;
+        final Uri packageUri = Uri.parse("package:" + appInfo.packageName);
+        final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
+        return uninstallIntent;
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        unregisterPackageRemoveReceiver();
+    }
+
     private void onAllowButtonFromEnableToggleClicked() {
         if (isFullDiskEncrypted()) {
             final String title = createConfirmCredentialReasonMessage();