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