1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.emergency.preferences; 17 18 import android.app.AlertDialog; 19 import android.app.Fragment; 20 import android.content.Context; 21 import android.content.DialogInterface; 22 import android.content.Intent; 23 import android.graphics.Bitmap; 24 import android.graphics.drawable.Drawable; 25 import android.os.AsyncTask; 26 import android.os.Bundle; 27 import android.os.UserHandle; 28 import android.os.UserManager; 29 import android.text.TextUtils; 30 import android.util.AttributeSet; 31 import android.view.View; 32 import android.widget.EditText; 33 import android.widget.ImageView; 34 import androidx.annotation.VisibleForTesting; 35 import androidx.preference.DialogPreference; 36 import com.android.emergency.CircleFramedDrawable; 37 import com.android.emergency.R; 38 import com.android.internal.util.UserIcons; 39 import com.android.settingslib.CustomDialogPreference; 40 41 import java.io.File; 42 43 /** 44 * Custom {@link DialogPreference} that allows us to editing the user name and photo. 45 */ 46 public class EmergencyNamePreference extends CustomDialogPreference { 47 48 private static final String KEY_AWAITING_RESULT = "awaiting_result"; 49 private static final String KEY_SAVED_PHOTO = "pending_photo"; 50 51 private UserManager mUserManager = getContext().getSystemService(UserManager.class); 52 private EditUserPhotoController mEditUserPhotoController; 53 private Fragment mFragment; 54 private Bitmap mSavedPhoto; 55 private EditText mUserNameView; 56 private ImageView mUserPhotoView; 57 private boolean mWaitingForActivityResult = false; 58 EmergencyNamePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)59 public EmergencyNamePreference(Context context, AttributeSet attrs, 60 int defStyleAttr, int defStyleRes) { 61 super(context, attrs, defStyleAttr, defStyleRes); 62 setSummary(mUserManager.getUserName()); 63 setIcon(getCircularUserIcon()); 64 setDialogLayoutResource(R.layout.edit_user_info_dialog_content); 65 } 66 EmergencyNamePreference(Context context, AttributeSet attrs, int defStyleAttr)67 public EmergencyNamePreference(Context context, AttributeSet attrs, int defStyleAttr) { 68 this(context, attrs, defStyleAttr, 0); 69 } 70 EmergencyNamePreference(Context context, AttributeSet attrs)71 public EmergencyNamePreference(Context context, AttributeSet attrs) { 72 this(context, attrs, R.attr.dialogPreferenceStyle); 73 } 74 EmergencyNamePreference(Context context)75 public EmergencyNamePreference(Context context) { 76 this(context, null); 77 } 78 79 /** 80 * Setup fragment for Dialog and EditUserPhotoController. 81 */ setFragment(Fragment fragment)82 public void setFragment(Fragment fragment) { 83 mFragment = fragment; 84 } 85 86 /** 87 * Reload user name and photo form UserManager. 88 */ reloadFromUserManager()89 public void reloadFromUserManager() { 90 setSummary(mUserManager.getUserName()); 91 setIcon(getCircularUserIcon()); 92 } 93 94 /** 95 * Restore user photo when EditUserPhotoController had pending photo. 96 */ onRestoreInstanceState(Bundle icicle)97 public void onRestoreInstanceState(Bundle icicle) { 98 String pendingPhoto = icicle.getString(KEY_SAVED_PHOTO); 99 if (pendingPhoto != null) { 100 mSavedPhoto = EditUserPhotoController.loadNewUserPhotoBitmap(new File(pendingPhoto)); 101 } 102 mWaitingForActivityResult = icicle.getBoolean(KEY_AWAITING_RESULT, false); 103 } 104 105 /** 106 * Save a temp user photo when layout need to recreating but Dialog is showing. 107 */ onSaveInstanceState(Bundle outState)108 public void onSaveInstanceState(Bundle outState) { 109 if (getDialog() != null && getDialog().isShowing() 110 && mEditUserPhotoController != null) { 111 // Bitmap cannot be stored into bundle because it may exceed parcel limit 112 // Store it in a temporary file instead 113 File file = mEditUserPhotoController.saveNewUserPhotoBitmap(); 114 if (file != null) { 115 outState.putString(KEY_SAVED_PHOTO, file.getPath()); 116 } 117 } 118 if (mWaitingForActivityResult) { 119 outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult); 120 } 121 } 122 123 /** 124 * Set mWaitingForActivityResult to true when EmergencyNamePreferenceDialogFragment 125 * startActivityForResult, means we are waiting the activity result. 126 */ startingActivityForResult()127 public void startingActivityForResult() { 128 mWaitingForActivityResult = true; 129 } 130 131 /** 132 * Reset mWaitingForActivityResult and send the result code to EditUserPhotoController when 133 * EmergencyNamePreferenceDialogFragment onActivityResult. 134 */ onActivityResult(int requestCode, int resultCode, Intent data)135 public void onActivityResult(int requestCode, int resultCode, Intent data) { 136 mWaitingForActivityResult = false; 137 138 if (getDialog() != null) { 139 mEditUserPhotoController.onActivityResult(requestCode, resultCode, data); 140 } 141 } 142 143 @Override onBindDialogView(View view)144 protected void onBindDialogView(View view) { 145 super.onBindDialogView(view); 146 147 mUserNameView = view.findViewById(R.id.user_name); 148 mUserNameView.setText(mUserManager.getUserName()); 149 mUserPhotoView = view.findViewById(R.id.user_photo); 150 Drawable drawable; 151 if (mSavedPhoto != null) { 152 drawable = CircleFramedDrawable.getInstance(getContext(), mSavedPhoto); 153 } else { 154 drawable = getCircularUserIcon(); 155 } 156 mUserPhotoView.setImageDrawable(drawable); 157 158 mEditUserPhotoController = createEditUserPhotoController(mUserPhotoView, 159 getCircularUserIcon()); 160 } 161 162 @Override onPrepareDialogBuilder(AlertDialog.Builder builder, DialogInterface.OnClickListener listener)163 protected void onPrepareDialogBuilder(AlertDialog.Builder builder, 164 DialogInterface.OnClickListener listener) { 165 super.onPrepareDialogBuilder(builder, listener); 166 builder.setTitle(R.string.name) 167 .setCancelable(true) 168 .setPositiveButton(android.R.string.ok, listener) 169 .setNegativeButton(android.R.string.cancel, listener) 170 .create(); 171 } 172 173 @Override onDialogClosed(boolean positiveResult)174 protected void onDialogClosed(boolean positiveResult) { 175 super.onDialogClosed(positiveResult); 176 if (positiveResult) { 177 // Update the name if changed. 178 CharSequence userName = mUserNameView.getText(); 179 if (!TextUtils.isEmpty(userName)) { 180 if (mUserManager.getUserName() == null 181 || !userName.toString().equals(mUserManager.getUserName())) { 182 mUserManager.setUserName(UserHandle.myUserId(), userName.toString()); 183 setSummary(userName); 184 } 185 } 186 // Update the photo if changed. 187 Drawable drawable = mEditUserPhotoController.getNewUserPhotoDrawable(); 188 Bitmap bitmap = mEditUserPhotoController.getNewUserPhotoBitmap(); 189 if (drawable != null && bitmap != null 190 && !drawable.equals(getCircularUserIcon())) { 191 new AsyncTask<Void, Void, Void>() { 192 @Override 193 protected Void doInBackground(Void... params) { 194 mUserManager.setUserIcon(UserHandle.myUserId(), 195 mEditUserPhotoController.getNewUserPhotoBitmap()); 196 return null; 197 } 198 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); 199 setIcon(drawable); 200 } 201 if (mFragment != null) { 202 mFragment.getActivity().removeDialog(1); 203 } 204 } 205 clear(); 206 } 207 clear()208 private void clear() { 209 mEditUserPhotoController.removeNewUserPhotoBitmapFile(); 210 mSavedPhoto = null; 211 } 212 getCircularUserIcon()213 private Drawable getCircularUserIcon() { 214 Bitmap bitmapUserIcon = mUserManager.getUserIcon(UserHandle.myUserId()); 215 216 if (bitmapUserIcon == null) { 217 // get default user icon. 218 final Drawable defaultUserIcon = UserIcons.getDefaultUserIcon( 219 getContext().getResources(), UserHandle.myUserId(), false); 220 bitmapUserIcon = UserIcons.convertToBitmap(defaultUserIcon); 221 } 222 Drawable drawableUserIcon = new CircleFramedDrawable(bitmapUserIcon, 223 (int) getContext().getResources().getDimension(R.dimen.circle_avatar_size)); 224 225 return drawableUserIcon; 226 } 227 228 @VisibleForTesting createEditUserPhotoController(ImageView userPhotoView, Drawable drawable)229 EditUserPhotoController createEditUserPhotoController(ImageView userPhotoView, 230 Drawable drawable) { 231 return new EditUserPhotoController(mFragment, userPhotoView, 232 mSavedPhoto, drawable, mWaitingForActivityResult); 233 } 234 235 public static class EmergencyNamePreferenceDialogFragment extends 236 CustomPreferenceDialogFragment { 237 newInstance( String key)238 public static CustomDialogPreference.CustomPreferenceDialogFragment newInstance( 239 String key) { 240 final CustomDialogPreference.CustomPreferenceDialogFragment 241 fragment = new EmergencyNamePreferenceDialogFragment(); 242 final Bundle b = new Bundle(1 /* capacity */); 243 b.putString(ARG_KEY, key); 244 fragment.setArguments(b); 245 return fragment; 246 } 247 getEmergencyNamePreference()248 private EmergencyNamePreference getEmergencyNamePreference() { 249 return (EmergencyNamePreference) getPreference(); 250 } 251 252 @Override onCreate(Bundle icicle)253 public void onCreate(Bundle icicle) { 254 super.onCreate(icicle); 255 getEmergencyNamePreference().setFragment(this); 256 if (icicle != null) { 257 getEmergencyNamePreference().onRestoreInstanceState(icicle); 258 } 259 } 260 261 @Override onSaveInstanceState(Bundle outState)262 public void onSaveInstanceState(Bundle outState) { 263 super.onSaveInstanceState(outState); 264 getEmergencyNamePreference().onSaveInstanceState(outState); 265 } 266 267 @Override onActivityResult(int requestCode, int resultCode, Intent data)268 public void onActivityResult(int requestCode, int resultCode, Intent data) { 269 getEmergencyNamePreference().onActivityResult(requestCode, resultCode, data); 270 } 271 272 @Override startActivityForResult(Intent intent, int requestCode)273 public void startActivityForResult(Intent intent, int requestCode) { 274 getEmergencyNamePreference().startingActivityForResult(); 275 super.startActivityForResult(intent, requestCode); 276 } 277 } 278 } 279