• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.settingslib.users;
18 
19 import android.app.Activity;
20 import android.content.Intent;
21 import android.content.res.Resources;
22 import android.graphics.Bitmap;
23 import android.graphics.BitmapFactory;
24 import android.graphics.drawable.Drawable;
25 import android.net.Uri;
26 import android.util.Log;
27 import android.widget.ImageView;
28 
29 import com.android.internal.util.UserIcons;
30 import com.android.settingslib.drawable.CircleFramedDrawable;
31 import com.android.settingslib.utils.ThreadUtils;
32 
33 import java.io.File;
34 import java.io.FileNotFoundException;
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.OutputStream;
39 import java.util.concurrent.ExecutionException;
40 
41 /**
42  * This class contains logic for starting activities to take/choose/crop photo, reads and transforms
43  * the result image.
44  */
45 public class EditUserPhotoController {
46     private static final String TAG = "EditUserPhotoController";
47 
48     // It seems that this class generates custom request codes and they may
49     // collide with ours, these values are very unlikely to have a conflict.
50     private static final int REQUEST_CODE_PICK_AVATAR = 1004;
51 
52     private static final String IMAGES_DIR = "multi_user";
53     private static final String NEW_USER_PHOTO_FILE_NAME = "NewUserPhoto.png";
54 
55     private final Activity mActivity;
56     private final ActivityStarter mActivityStarter;
57     private final ImageView mImageView;
58     private final String mFileAuthority;
59 
60     private final File mImagesDir;
61     private Bitmap mNewUserPhotoBitmap;
62     private Drawable mNewUserPhotoDrawable;
63 
EditUserPhotoController(Activity activity, ActivityStarter activityStarter, ImageView view, Bitmap savedBitmap, Drawable savedDrawable, String fileAuthority)64     public EditUserPhotoController(Activity activity, ActivityStarter activityStarter,
65             ImageView view, Bitmap savedBitmap, Drawable savedDrawable, String fileAuthority) {
66         mActivity = activity;
67         mActivityStarter = activityStarter;
68         mFileAuthority = fileAuthority;
69 
70         mImagesDir = new File(activity.getCacheDir(), IMAGES_DIR);
71         mImagesDir.mkdir();
72         mImageView = view;
73         mImageView.setOnClickListener(v -> showAvatarPicker());
74 
75         mNewUserPhotoBitmap = savedBitmap;
76         mNewUserPhotoDrawable = savedDrawable;
77     }
78 
79     /**
80      * Handles activity result from containing activity/fragment after a take/choose/crop photo
81      * action result is received.
82      */
onActivityResult(int requestCode, int resultCode, Intent data)83     public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
84         if (resultCode != Activity.RESULT_OK) {
85             return false;
86         }
87 
88         if (requestCode == REQUEST_CODE_PICK_AVATAR) {
89             if (data.hasExtra(AvatarPickerActivity.EXTRA_DEFAULT_ICON_TINT_COLOR)) {
90                 int tintColor =
91                         data.getIntExtra(AvatarPickerActivity.EXTRA_DEFAULT_ICON_TINT_COLOR, -1);
92                 onDefaultIconSelected(tintColor);
93                 return true;
94             }
95             if (data.getData() != null) {
96                 onPhotoCropped(data.getData());
97                 return true;
98             }
99 
100         }
101         return false;
102     }
103 
getNewUserPhotoDrawable()104     public Drawable getNewUserPhotoDrawable() {
105         return mNewUserPhotoDrawable;
106     }
107 
showAvatarPicker()108     private void showAvatarPicker() {
109         Intent intent = new Intent(mImageView.getContext(), AvatarPickerActivity.class);
110         intent.putExtra(AvatarPickerActivity.EXTRA_FILE_AUTHORITY, mFileAuthority);
111         mActivityStarter.startActivityForResult(intent, REQUEST_CODE_PICK_AVATAR);
112     }
113 
onDefaultIconSelected(int tintColor)114     private void onDefaultIconSelected(int tintColor) {
115         try {
116             ThreadUtils.postOnBackgroundThread(() -> {
117                 Resources res = mActivity.getResources();
118                 Drawable drawable =
119                         UserIcons.getDefaultUserIconInColor(res, tintColor);
120                 Bitmap bitmap = UserIcons.convertToBitmapAtUserIconSize(res, drawable);
121 
122                 ThreadUtils.postOnMainThread(() -> onPhotoProcessed(bitmap));
123             }).get();
124         } catch (InterruptedException | ExecutionException e) {
125             Log.e(TAG, "Error processing default icon", e);
126         }
127     }
128 
onPhotoCropped(final Uri data)129     private void onPhotoCropped(final Uri data) {
130         ThreadUtils.postOnBackgroundThread(() -> {
131             InputStream imageStream = null;
132             Bitmap bitmap = null;
133             try {
134                 imageStream = mActivity.getContentResolver()
135                         .openInputStream(data);
136                 bitmap = BitmapFactory.decodeStream(imageStream);
137             } catch (FileNotFoundException fe) {
138                 Log.w(TAG, "Cannot find image file", fe);
139             } finally {
140                 if (imageStream != null) {
141                     try {
142                         imageStream.close();
143                     } catch (IOException ioe) {
144                         Log.w(TAG, "Cannot close image stream", ioe);
145                     }
146                 }
147             }
148 
149             if (bitmap != null) {
150                 Bitmap finalBitmap = bitmap;
151                 ThreadUtils.postOnMainThread(() -> onPhotoProcessed(finalBitmap));
152             }
153         });
154     }
155 
onPhotoProcessed(Bitmap bitmap)156     private void onPhotoProcessed(Bitmap bitmap) {
157         if (bitmap != null) {
158             mNewUserPhotoBitmap = bitmap;
159             mNewUserPhotoDrawable = CircleFramedDrawable
160                     .getInstance(mImageView.getContext(), mNewUserPhotoBitmap);
161             mImageView.setImageDrawable(mNewUserPhotoDrawable);
162         }
163     }
164 
saveNewUserPhotoBitmap()165     File saveNewUserPhotoBitmap() {
166         if (mNewUserPhotoBitmap == null) {
167             return null;
168         }
169         try {
170             File file = new File(mImagesDir, NEW_USER_PHOTO_FILE_NAME);
171             OutputStream os = new FileOutputStream(file);
172             mNewUserPhotoBitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
173             os.flush();
174             os.close();
175             return file;
176         } catch (IOException e) {
177             Log.e(TAG, "Cannot create temp file", e);
178         }
179         return null;
180     }
181 
loadNewUserPhotoBitmap(File file)182     static Bitmap loadNewUserPhotoBitmap(File file) {
183         return BitmapFactory.decodeFile(file.getAbsolutePath());
184     }
185 
removeNewUserPhotoBitmapFile()186     void removeNewUserPhotoBitmapFile() {
187         new File(mImagesDir, NEW_USER_PHOTO_FILE_NAME).delete();
188     }
189 }
190