1 /* 2 * Copyright (C) 2013 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 17 package com.android.settings.users; 18 19 import android.app.Activity; 20 import android.app.Dialog; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.Intent; 24 import android.content.pm.UserInfo; 25 import android.graphics.Bitmap; 26 import android.graphics.drawable.Drawable; 27 import android.os.Bundle; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 import android.text.TextUtils; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.WindowManager; 34 import android.widget.EditText; 35 import android.widget.ImageView; 36 37 import androidx.annotation.VisibleForTesting; 38 import androidx.appcompat.app.AlertDialog; 39 import androidx.fragment.app.Fragment; 40 41 import com.android.settings.R; 42 import com.android.settingslib.drawable.CircleFramedDrawable; 43 44 import java.io.File; 45 46 /** 47 * This class encapsulates a Dialog for editing the user nickname and photo. 48 */ 49 public class EditUserInfoController { 50 51 private static final String KEY_AWAITING_RESULT = "awaiting_result"; 52 private static final String KEY_SAVED_PHOTO = "pending_photo"; 53 54 private Dialog mEditUserInfoDialog; 55 private Bitmap mSavedPhoto; 56 private EditUserPhotoController mEditUserPhotoController; 57 private UserHandle mUser; 58 private UserManager mUserManager; 59 private boolean mWaitingForActivityResult = false; 60 61 /** 62 * Callback made when either the username text or photo choice changes. 63 */ 64 public interface OnContentChangedCallback { 65 /** Photo updated. */ onPhotoChanged(UserHandle user, Drawable photo)66 void onPhotoChanged(UserHandle user, Drawable photo); 67 /** Username updated. */ onLabelChanged(UserHandle user, CharSequence label)68 void onLabelChanged(UserHandle user, CharSequence label); 69 } 70 71 /** 72 * Callback made when the dialog finishes. 73 */ 74 public interface OnDialogCompleteCallback { 75 /** Dialog closed with positive button. */ onPositive()76 void onPositive(); 77 /** Dialog closed with negative button or cancelled. */ onNegativeOrCancel()78 void onNegativeOrCancel(); 79 } 80 clear()81 public void clear() { 82 if (mEditUserPhotoController != null) { 83 mEditUserPhotoController.removeNewUserPhotoBitmapFile(); 84 } 85 mEditUserInfoDialog = null; 86 mSavedPhoto = null; 87 } 88 getDialog()89 public Dialog getDialog() { 90 return mEditUserInfoDialog; 91 } 92 onRestoreInstanceState(Bundle icicle)93 public void onRestoreInstanceState(Bundle icicle) { 94 String pendingPhoto = icicle.getString(KEY_SAVED_PHOTO); 95 if (pendingPhoto != null) { 96 mSavedPhoto = EditUserPhotoController.loadNewUserPhotoBitmap(new File(pendingPhoto)); 97 } 98 mWaitingForActivityResult = icicle.getBoolean(KEY_AWAITING_RESULT, false); 99 } 100 onSaveInstanceState(Bundle outState)101 public void onSaveInstanceState(Bundle outState) { 102 if (mEditUserInfoDialog != null && mEditUserInfoDialog.isShowing() 103 && mEditUserPhotoController != null) { 104 // Bitmap cannot be stored into bundle because it may exceed parcel limit 105 // Store it in a temporary file instead 106 File file = mEditUserPhotoController.saveNewUserPhotoBitmap(); 107 if (file != null) { 108 outState.putString(KEY_SAVED_PHOTO, file.getPath()); 109 } 110 } 111 if (mWaitingForActivityResult) { 112 outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult); 113 } 114 } 115 startingActivityForResult()116 public void startingActivityForResult() { 117 mWaitingForActivityResult = true; 118 } 119 onActivityResult(int requestCode, int resultCode, Intent data)120 public void onActivityResult(int requestCode, int resultCode, Intent data) { 121 mWaitingForActivityResult = false; 122 123 if (mEditUserPhotoController != null && mEditUserInfoDialog != null) { 124 mEditUserPhotoController.onActivityResult(requestCode, resultCode, data); 125 } 126 } 127 createDialog(final Fragment fragment, final Drawable currentUserIcon, final CharSequence currentUserName, String title, final OnContentChangedCallback callback, UserHandle user, OnDialogCompleteCallback completeCallback)128 public Dialog createDialog(final Fragment fragment, final Drawable currentUserIcon, 129 final CharSequence currentUserName, 130 String title, final OnContentChangedCallback callback, UserHandle user, 131 OnDialogCompleteCallback completeCallback) { 132 Activity activity = fragment.getActivity(); 133 mUser = user; 134 if (mUserManager == null) { 135 mUserManager = activity.getSystemService(UserManager.class); 136 } 137 LayoutInflater inflater = activity.getLayoutInflater(); 138 View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null); 139 140 final EditText userNameView = (EditText) content.findViewById(R.id.user_name); 141 userNameView.setText(currentUserName); 142 143 final ImageView userPhotoView = (ImageView) content.findViewById(R.id.user_photo); 144 145 boolean canChangePhoto = mUserManager != null && 146 canChangePhoto(activity, mUserManager.getUserInfo(user.getIdentifier())); 147 if (!canChangePhoto) { 148 // some users can't change their photos so we need to remove suggestive 149 // background from the photoView 150 userPhotoView.setBackground(null); 151 } 152 Drawable drawable = null; 153 if (mSavedPhoto != null) { 154 drawable = CircleFramedDrawable.getInstance(activity, mSavedPhoto); 155 } else { 156 drawable = currentUserIcon; 157 } 158 userPhotoView.setImageDrawable(drawable); 159 if (canChangePhoto) { 160 mEditUserPhotoController = 161 createEditUserPhotoController(fragment, userPhotoView, drawable); 162 } 163 mEditUserInfoDialog = new AlertDialog.Builder(activity) 164 .setTitle(title) 165 .setView(content) 166 .setCancelable(true) 167 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 168 @Override 169 public void onClick(DialogInterface dialog, int which) { 170 if (which == DialogInterface.BUTTON_POSITIVE) { 171 // Update the name if changed. 172 CharSequence userName = userNameView.getText(); 173 if (!TextUtils.isEmpty(userName)) { 174 if (currentUserName == null 175 || !userName.toString().equals( 176 currentUserName.toString())) { 177 if (callback != null) { 178 callback.onLabelChanged(mUser, userName.toString()); 179 } 180 } 181 } 182 // Update the photo if changed. 183 if (mEditUserPhotoController != null) { 184 Drawable drawable = 185 mEditUserPhotoController.getNewUserPhotoDrawable(); 186 if (drawable != null && !drawable.equals(currentUserIcon)) { 187 if (callback != null) { 188 callback.onPhotoChanged(mUser, drawable); 189 } 190 } 191 } 192 } 193 clear(); 194 if (completeCallback != null) { 195 completeCallback.onPositive(); 196 } 197 } 198 }) 199 .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 200 @Override 201 public void onClick(DialogInterface dialog, int which) { 202 clear(); 203 if (completeCallback != null) { 204 completeCallback.onNegativeOrCancel(); 205 } 206 } 207 }) 208 .setOnCancelListener(new DialogInterface.OnCancelListener() { 209 @Override 210 public void onCancel(DialogInterface dialog) { 211 clear(); 212 if (completeCallback != null) { 213 completeCallback.onNegativeOrCancel(); 214 } 215 } 216 }) 217 .create(); 218 219 // Make sure the IME is up. 220 mEditUserInfoDialog.getWindow().setSoftInputMode( 221 WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); 222 223 return mEditUserInfoDialog; 224 } 225 226 @VisibleForTesting canChangePhoto(Context context, UserInfo user)227 boolean canChangePhoto(Context context, UserInfo user) { 228 return PhotoCapabilityUtils.canCropPhoto(context) && 229 (PhotoCapabilityUtils.canChoosePhoto(context) 230 || PhotoCapabilityUtils.canTakePhoto(context)); 231 } 232 233 @VisibleForTesting createEditUserPhotoController(Fragment fragment, ImageView userPhotoView, Drawable drawable)234 EditUserPhotoController createEditUserPhotoController(Fragment fragment, 235 ImageView userPhotoView, Drawable drawable) { 236 return new EditUserPhotoController(fragment, userPhotoView, 237 mSavedPhoto, drawable, mWaitingForActivityResult); 238 } 239 } 240