1 package com.android.packageinstaller.permission.ui; 2 3 import static com.android.packageinstaller.permission.ui.GrantPermissionsActivity.LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON; 4 5 import android.app.AlertDialog; 6 import android.app.Dialog; 7 import android.content.Context; 8 import android.content.DialogInterface; 9 import android.content.res.TypedArray; 10 import android.graphics.PixelFormat; 11 import android.graphics.drawable.Drawable; 12 import android.graphics.drawable.Icon; 13 import android.os.Bundle; 14 import android.text.SpannableStringBuilder; 15 import android.text.Spanned; 16 import android.text.TextUtils; 17 import android.text.style.TextAppearanceSpan; 18 import android.util.Log; 19 import android.view.View; 20 import android.view.WindowManager; 21 import android.widget.Space; 22 23 import androidx.wear.ble.view.AcceptDenyDialog; 24 import androidx.wear.ble.view.WearableDialogHelper; 25 26 import com.android.permissioncontroller.R; 27 28 /** 29 * Watch-specific view handler for the grant permissions activity. 30 */ 31 final class GrantPermissionsWatchViewHandler implements GrantPermissionsViewHandler, 32 DialogInterface.OnClickListener { 33 private static final String TAG = "GrantPermsWatchViewH"; 34 35 private static final String WATCH_HANDLER_BUNDLE = "watch_handler_bundle"; 36 private static final String DIALOG_BUNDLE = "dialog_bundle"; 37 private static final String GROUP_NAME = "group_name"; 38 private static final String SHOW_DO_NOT_ASK = "show_do_not_ask"; 39 private static final String ICON = "icon"; 40 private static final String MESSAGE = "message"; 41 private static final String CURRENT_PAGE_TEXT = "current_page_text"; 42 43 private final Context mContext; 44 45 private ResultListener mResultListener; 46 47 private Dialog mDialog; 48 49 private String mGroupName; 50 private boolean mShowDoNotAsk; 51 52 private CharSequence mMessage; 53 private String mCurrentPageText; 54 private Icon mIcon; 55 GrantPermissionsWatchViewHandler(Context context)56 GrantPermissionsWatchViewHandler(Context context) { 57 mContext = context; 58 } 59 60 @Override setResultListener(ResultListener listener)61 public GrantPermissionsWatchViewHandler setResultListener(ResultListener listener) { 62 mResultListener = listener; 63 return this; 64 } 65 66 @Override createView()67 public View createView() { 68 return new Space(mContext); 69 } 70 71 @Override updateWindowAttributes(WindowManager.LayoutParams outLayoutParams)72 public void updateWindowAttributes(WindowManager.LayoutParams outLayoutParams) { 73 outLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; 74 outLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; 75 outLayoutParams.format = PixelFormat.OPAQUE; 76 outLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; 77 outLayoutParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; 78 } 79 80 @Override updateUi(String groupName, int groupCount, int groupIndex, Icon icon, CharSequence message, CharSequence detailMessage, CharSequence[] buttonLabels)81 public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon, 82 CharSequence message, CharSequence detailMessage, 83 CharSequence[] buttonLabels) { 84 // TODO: Handle detailMessage 85 86 boolean showDoNotAsk = buttonLabels[LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON] != null; 87 88 if (Log.isLoggable(TAG, Log.DEBUG)) { 89 Log.d(TAG, "updateUi() - groupName: " + groupName 90 + ", groupCount: " + groupCount 91 + ", groupIndex: " + groupIndex 92 + ", icon: " + icon 93 + ", message: " + message 94 + ", showDoNotAsk: " + showDoNotAsk); 95 } 96 97 mGroupName = groupName; 98 mShowDoNotAsk = showDoNotAsk; 99 mMessage = message; 100 mIcon = icon; 101 mCurrentPageText = groupCount > 1 102 ? mContext.getString(R.string.current_permission_template, 103 groupIndex + 1, groupCount) 104 : null; 105 showDialog(null); 106 } 107 showDialog(Bundle savedInstanceState)108 private void showDialog(Bundle savedInstanceState) { 109 TypedArray a = mContext.obtainStyledAttributes( 110 new int[] { android.R.attr.textColorPrimary }); 111 int color = a.getColor(0, mContext.getColor(android.R.color.white)); 112 a.recycle(); 113 Drawable drawable = mIcon == null ? null : mIcon.setTint(color).loadDrawable(mContext); 114 115 SpannableStringBuilder ssb = new SpannableStringBuilder(); 116 if (!TextUtils.isEmpty(mCurrentPageText)) { 117 ssb.append(mCurrentPageText, new TextAppearanceSpan(mContext, R.style.BreadcrumbText), 118 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 119 ssb.append('\n'); 120 } 121 if (!TextUtils.isEmpty(mMessage)) { 122 ssb.append(mMessage, new TextAppearanceSpan(mContext, R.style.TitleText), 123 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 124 } 125 126 if (mDialog != null) { 127 mDialog.dismiss(); 128 mDialog = null; 129 } 130 131 if (mShowDoNotAsk) { 132 AlertDialog alertDialog = new WearableDialogHelper.DialogBuilder(mContext) 133 .setPositiveIcon(R.drawable.confirm_button) 134 .setNeutralIcon(R.drawable.cancel_button) 135 .setNegativeIcon(R.drawable.deny_button) 136 .setTitle(ssb) 137 .setIcon(drawable) 138 .setPositiveButton(R.string.grant_dialog_button_allow, this) 139 .setNeutralButton(R.string.grant_dialog_button_deny, this) 140 .setNegativeButton(R.string.grant_dialog_button_deny_dont_ask_again, this) 141 .show(); 142 alertDialog.getButton(DialogInterface.BUTTON_POSITIVE) 143 .setId(R.id.permission_allow_button); 144 alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL) 145 .setId(R.id.permission_deny_button); 146 alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE) 147 .setId(R.id.permission_deny_dont_ask_again_button); 148 149 mDialog = alertDialog; 150 } else { 151 AcceptDenyDialog acceptDenyDialog = new AcceptDenyDialog(mContext); 152 acceptDenyDialog.setTitle(ssb); 153 acceptDenyDialog.setIcon(drawable); 154 acceptDenyDialog.setPositiveButton(this); 155 acceptDenyDialog.setNegativeButton(this); 156 acceptDenyDialog.show(); 157 acceptDenyDialog.getButton(DialogInterface.BUTTON_POSITIVE) 158 .setId(R.id.permission_allow_button); 159 acceptDenyDialog.getButton(DialogInterface.BUTTON_NEGATIVE) 160 .setId(R.id.permission_deny_button); 161 162 mDialog = acceptDenyDialog; 163 } 164 mDialog.setCancelable(false); 165 166 if (savedInstanceState != null) { 167 mDialog.onRestoreInstanceState(savedInstanceState); 168 } 169 } 170 171 @Override saveInstanceState(Bundle outState)172 public void saveInstanceState(Bundle outState) { 173 Bundle b = new Bundle(); 174 b.putByte(SHOW_DO_NOT_ASK, (byte) (mShowDoNotAsk ? 1 : 0)); 175 b.putString(GROUP_NAME, mGroupName); 176 b.putBundle(DIALOG_BUNDLE, mDialog.onSaveInstanceState()); 177 178 outState.putBundle(WATCH_HANDLER_BUNDLE, b); 179 } 180 181 @Override loadInstanceState(Bundle savedInstanceState)182 public void loadInstanceState(Bundle savedInstanceState) { 183 Bundle b = savedInstanceState.getBundle(WATCH_HANDLER_BUNDLE); 184 mShowDoNotAsk = b.getByte(SHOW_DO_NOT_ASK) == 1; 185 mGroupName = b.getString(GROUP_NAME); 186 showDialog(b.getBundle(DIALOG_BUNDLE)); 187 } 188 189 @Override onBackPressed()190 public void onBackPressed() { 191 notifyListener(DENIED); 192 } 193 194 @Override onClick(DialogInterface dialog, int which)195 public void onClick(DialogInterface dialog, int which) { 196 switch (which) { 197 case DialogInterface.BUTTON_POSITIVE: 198 notifyListener(GRANTED_ALWAYS); 199 break; 200 case DialogInterface.BUTTON_NEUTRAL: 201 notifyListener(DENIED); 202 break; 203 case DialogInterface.BUTTON_NEGATIVE: 204 /* In AlertDialog, the negative button is also a don't ask again button. */ 205 if (dialog instanceof AlertDialog) { 206 notifyListener(DENIED_DO_NOT_ASK_AGAIN); 207 } else { 208 notifyListener(DENIED); 209 } 210 break; 211 } 212 } 213 notifyListener(@esult int result)214 private void notifyListener(@Result int result) { 215 if (mResultListener != null) { 216 mResultListener.onPermissionGrantResult(mGroupName, result); 217 } 218 } 219 } 220