Add uninstall option in the capabilities confirm dialog
Uninstall option is visible only when the application of the service is not system App. It provides an efficient way to uninstall the App if users are shocked with the perimssions they need to grant. Bug: 176877955 Test: Install an App with AccessibilityService and uninstall it with this option. Change-Id: I6b60cc56d8d34c2983458a676b500035551ec9df
This commit is contained in:
@@ -134,6 +134,11 @@
|
|||||||
android:text="@string/accessibility_dialog_button_deny"
|
android:text="@string/accessibility_dialog_button_deny"
|
||||||
style="@style/AccessibilityDialogButton" />
|
style="@style/AccessibilityDialogButton" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/permission_enable_uninstall_button"
|
||||||
|
android:text="@string/uninstall_text"
|
||||||
|
android:visibility="gone"
|
||||||
|
style="@style/AccessibilityDialogButton" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@@ -35,6 +35,7 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
@@ -60,11 +61,19 @@ public class AccessibilityServiceWarning {
|
|||||||
return false;
|
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. */
|
/** Returns a {@link Dialog} to be shown to confirm that they want to enable a service. */
|
||||||
public static Dialog createCapabilitiesDialog(Context context,
|
public static Dialog createCapabilitiesDialog(@NonNull Context context,
|
||||||
AccessibilityServiceInfo info, View.OnClickListener listener) {
|
@NonNull AccessibilityServiceInfo info, @NonNull View.OnClickListener listener,
|
||||||
|
@NonNull UninstallActionPerformer performer) {
|
||||||
final AlertDialog ad = new AlertDialog.Builder(context)
|
final AlertDialog ad = new AlertDialog.Builder(context)
|
||||||
.setView(createEnableDialogContentView(context, info, listener))
|
.setView(createEnableDialogContentView(context, info, listener, performer))
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
Window window = ad.getWindow();
|
Window window = ad.getWindow();
|
||||||
@@ -88,7 +97,8 @@ public class AccessibilityServiceWarning {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static View createEnableDialogContentView(Context context,
|
private static View createEnableDialogContentView(Context context,
|
||||||
AccessibilityServiceInfo info, View.OnClickListener listener) {
|
@NonNull AccessibilityServiceInfo info, View.OnClickListener listener,
|
||||||
|
UninstallActionPerformer performer) {
|
||||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(
|
LayoutInflater inflater = (LayoutInflater) context.getSystemService(
|
||||||
Context.LAYOUT_INFLATER_SERVICE);
|
Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
|
||||||
@@ -129,6 +139,14 @@ public class AccessibilityServiceWarning {
|
|||||||
permissionAllowButton.setOnTouchListener(filterTouchListener);
|
permissionAllowButton.setOnTouchListener(filterTouchListener);
|
||||||
permissionDenyButton.setOnClickListener(listener);
|
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;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -381,4 +381,13 @@ final class AccessibilityUtil {
|
|||||||
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
|
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
|
||||||
resources.getDisplayMetrics()));
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,10 +24,14 @@ import android.app.Activity;
|
|||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.pm.ServiceInfo;
|
import android.content.pm.ServiceInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -37,11 +41,13 @@ import android.os.UserHandle;
|
|||||||
import android.os.storage.StorageManager;
|
import android.os.storage.StorageManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.SwitchPreference;
|
import androidx.preference.SwitchPreference;
|
||||||
|
|
||||||
@@ -58,7 +64,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
public class ToggleAccessibilityServicePreferenceFragment extends
|
public class ToggleAccessibilityServicePreferenceFragment extends
|
||||||
ToggleFeaturePreferenceFragment {
|
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 LockPatternUtils mLockPatternUtils;
|
||||||
private AtomicBoolean mIsDialogShown = new AtomicBoolean(/* initialValue= */ false);
|
private AtomicBoolean mIsDialogShown = new AtomicBoolean(/* initialValue= */ false);
|
||||||
|
|
||||||
@@ -73,6 +80,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
|||||||
};
|
};
|
||||||
|
|
||||||
private Dialog mDialog;
|
private Dialog mDialog;
|
||||||
|
private BroadcastReceiver mPackageRemovedReceiver;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMetricsCategory() {
|
public int getMetricsCategory() {
|
||||||
@@ -92,6 +100,17 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
|||||||
mLockPatternUtils = new LockPatternUtils(getPrefContext());
|
mLockPatternUtils = new LockPatternUtils(getPrefContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
final AccessibilityServiceInfo serviceInfo = getAccessibilityServiceInfo();
|
||||||
|
if (serviceInfo == null) {
|
||||||
|
getActivity().finishAndRemoveTask();
|
||||||
|
} else if (!AccessibilityUtil.isSystemApp(serviceInfo)) {
|
||||||
|
registerPackageRemoveReceiver();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
@@ -110,6 +129,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
|||||||
// capabilities. For
|
// capabilities. For
|
||||||
// example, before JellyBean MR2 the user was granting the explore by touch
|
// example, before JellyBean MR2 the user was granting the explore by touch
|
||||||
// one.
|
// one.
|
||||||
|
@Nullable
|
||||||
AccessibilityServiceInfo getAccessibilityServiceInfo() {
|
AccessibilityServiceInfo getAccessibilityServiceInfo() {
|
||||||
final List<AccessibilityServiceInfo> infos = AccessibilityManager.getInstance(
|
final List<AccessibilityServiceInfo> infos = AccessibilityManager.getInstance(
|
||||||
getPrefContext()).getInstalledAccessibilityServiceList();
|
getPrefContext()).getInstalledAccessibilityServiceList();
|
||||||
@@ -135,7 +155,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
|||||||
}
|
}
|
||||||
mDialog = AccessibilityServiceWarning
|
mDialog = AccessibilityServiceWarning
|
||||||
.createCapabilitiesDialog(getPrefContext(), info,
|
.createCapabilitiesDialog(getPrefContext(), info,
|
||||||
this::onDialogButtonFromEnableToggleClicked);
|
this::onDialogButtonFromEnableToggleClicked,
|
||||||
|
this::onDialogButtonFromUninstallClicked);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: {
|
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: {
|
||||||
@@ -145,7 +166,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
|||||||
}
|
}
|
||||||
mDialog = AccessibilityServiceWarning
|
mDialog = AccessibilityServiceWarning
|
||||||
.createCapabilitiesDialog(getPrefContext(), info,
|
.createCapabilitiesDialog(getPrefContext(), info,
|
||||||
this::onDialogButtonFromShortcutToggleClicked);
|
this::onDialogButtonFromShortcutToggleClicked,
|
||||||
|
this::onDialogButtonFromUninstallClicked);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: {
|
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: {
|
||||||
@@ -155,7 +177,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
|||||||
}
|
}
|
||||||
mDialog = AccessibilityServiceWarning
|
mDialog = AccessibilityServiceWarning
|
||||||
.createCapabilitiesDialog(getPrefContext(), info,
|
.createCapabilitiesDialog(getPrefContext(), info,
|
||||||
this::onDialogButtonFromShortcutClicked);
|
this::onDialogButtonFromShortcutClicked,
|
||||||
|
this::onDialogButtonFromUninstallClicked);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: {
|
case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: {
|
||||||
@@ -244,6 +267,32 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
private boolean isServiceSupportAccessibilityButton() {
|
||||||
final AccessibilityManager ams = getPrefContext().getSystemService(
|
final AccessibilityManager ams = getPrefContext().getSystemService(
|
||||||
AccessibilityManager.class);
|
AccessibilityManager.class);
|
||||||
@@ -375,6 +424,35 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
private void onAllowButtonFromEnableToggleClicked() {
|
||||||
if (isFullDiskEncrypted()) {
|
if (isFullDiskEncrypted()) {
|
||||||
final String title = createConfirmCredentialReasonMessage();
|
final String title = createConfirmCredentialReasonMessage();
|
||||||
|
Reference in New Issue
Block a user