/* * Copyright (C) 2016 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. */ package com.android.settings.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.os.storage.StorageManager; import android.text.BidiFormatter; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.android.settings.R; import java.util.List; import java.util.Locale; /** * Utility class for creating the dialog that asks users for explicit permission to grant * all of the requested capabilities to an accessibility service before the service is enabled */ public class AccessibilityServiceWarning { public static Dialog createCapabilitiesDialog(Activity parentActivity, AccessibilityServiceInfo info, DialogInterface.OnClickListener listener) { final AlertDialog ad = new AlertDialog.Builder(parentActivity) .setTitle(parentActivity.getString(R.string.enable_service_title, getServiceName(parentActivity, info))) .setView(createEnableDialogContentView(parentActivity, info)) .setPositiveButton(android.R.string.ok, listener) .setNegativeButton(android.R.string.cancel, listener) .create(); final View.OnTouchListener filterTouchListener = (View v, MotionEvent event) -> { // Filter obscured touches by consuming them. if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) { if (event.getAction() == MotionEvent.ACTION_UP) { Toast.makeText(v.getContext(), R.string.touch_filtered_warning, Toast.LENGTH_SHORT).show(); } return true; } return false; }; ad.create(); ad.getButton(AlertDialog.BUTTON_POSITIVE).setOnTouchListener(filterTouchListener); ad.setCanceledOnTouchOutside(true); return ad; } /** * Return whether the device is encrypted with legacy full disk encryption. Newer devices * should be using File Based Encryption. * * @return true if device is encrypted */ private static boolean isFullDiskEncrypted() { return StorageManager.isNonDefaultBlockEncrypted(); } /** * Get a content View for a dialog to confirm that they want to enable a service. * * @param context A valid context * @param info The info about a service * @return A content view suitable for viewing */ private static View createEnableDialogContentView(Context context, AccessibilityServiceInfo info) { LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE); View content = inflater.inflate(R.layout.enable_accessibility_service_dialog_content, null); TextView encryptionWarningView = (TextView) content.findViewById( R.id.encryption_warning); if (isFullDiskEncrypted()) { String text = context.getString(R.string.enable_service_encryption_warning, getServiceName(context, info)); encryptionWarningView.setText(text); encryptionWarningView.setVisibility(View.VISIBLE); } else { encryptionWarningView.setVisibility(View.GONE); } TextView capabilitiesHeaderView = (TextView) content.findViewById( R.id.capabilities_header); capabilitiesHeaderView.setText(context.getString(R.string.capabilities_list_title, getServiceName(context, info))); LinearLayout capabilitiesView = (LinearLayout) content.findViewById(R.id.capabilities); // This capability is implicit for all services. View capabilityView = inflater.inflate( com.android.internal.R.layout.app_permission_item_old, null); ImageView imageView = (ImageView) capabilityView.findViewById( com.android.internal.R.id.perm_icon); imageView.setImageDrawable(context.getDrawable( com.android.internal.R.drawable.ic_text_dot)); TextView labelView = (TextView) capabilityView.findViewById( com.android.internal.R.id.permission_group); labelView.setText(context.getString( R.string.capability_title_receiveAccessibilityEvents)); TextView descriptionView = (TextView) capabilityView.findViewById( com.android.internal.R.id.permission_list); descriptionView.setText( context.getString(R.string.capability_desc_receiveAccessibilityEvents)); List capabilities = info.getCapabilityInfos(context); capabilitiesView.addView(capabilityView); // Service-specific capabilities. final int capabilityCount = capabilities.size(); for (int i = 0; i < capabilityCount; i++) { AccessibilityServiceInfo.CapabilityInfo capability = capabilities.get(i); capabilityView = inflater.inflate( com.android.internal.R.layout.app_permission_item_old, null); imageView = (ImageView) capabilityView.findViewById( com.android.internal.R.id.perm_icon); imageView.setImageDrawable(context.getDrawable( com.android.internal.R.drawable.ic_text_dot)); labelView = (TextView) capabilityView.findViewById( com.android.internal.R.id.permission_group); labelView.setText(context.getString(capability.titleResId)); descriptionView = (TextView) capabilityView.findViewById( com.android.internal.R.id.permission_list); descriptionView.setText(context.getString(capability.descResId)); capabilitiesView.addView(capabilityView); } return content; } // Get the service name and bidi wrap it to protect from bidi side effects. private static CharSequence getServiceName(Context context, AccessibilityServiceInfo info) { final Locale locale = context.getResources().getConfiguration().getLocales().get(0); final CharSequence label = info.getResolveInfo().loadLabel(context.getPackageManager()); return BidiFormatter.getInstance(locale).unicodeWrap(label); } }