blob: ce691c3b7fb4074dc7610f3c32c46fe3a661f6df [file] [log] [blame]
Josh Garguse5d3f892012-04-11 11:56:15 -07001/*
2 * Copyright (C) 2012 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
18package com.android.contacts.util;
19
Yorke Lee637a38e2013-09-14 08:36:33 -070020import android.content.ClipData;
Josh Gargusebc17922012-05-04 18:47:09 -070021import android.content.Context;
Chiao Cheng61414c22012-06-18 16:59:06 -070022import android.content.Intent;
Josh Garguse5d3f892012-04-11 11:56:15 -070023import android.graphics.Bitmap;
Yorke Lee637a38e2013-09-14 08:36:33 -070024import android.graphics.BitmapFactory;
Chiao Cheng61414c22012-06-18 16:59:06 -070025import android.net.Uri;
Josh Garguse5d3f892012-04-11 11:56:15 -070026import android.os.Environment;
Chiao Cheng61414c22012-06-18 16:59:06 -070027import android.provider.MediaStore;
Yorke Lee637a38e2013-09-14 08:36:33 -070028import android.support.v4.content.FileProvider;
Josh Garguse5d3f892012-04-11 11:56:15 -070029import android.util.Log;
30
Paul Soulosfc98ff92014-09-12 10:45:43 -070031import com.android.contacts.R;
Yorke Lee637a38e2013-09-14 08:36:33 -070032import com.google.common.io.Closeables;
33
Josh Garguse5d3f892012-04-11 11:56:15 -070034import java.io.ByteArrayOutputStream;
35import java.io.File;
Yorke Lee637a38e2013-09-14 08:36:33 -070036import java.io.FileNotFoundException;
37import java.io.FileOutputStream;
Josh Garguse5d3f892012-04-11 11:56:15 -070038import java.io.IOException;
Yorke Lee637a38e2013-09-14 08:36:33 -070039import java.io.InputStream;
Josh Garguse5d3f892012-04-11 11:56:15 -070040import java.text.SimpleDateFormat;
41import java.util.Date;
Johan Redestig9bcfc322012-10-05 08:23:51 +020042import java.util.Locale;
Josh Garguse5d3f892012-04-11 11:56:15 -070043
44/**
45 * Utilities related to loading/saving contact photos.
46 *
47 */
48public class ContactPhotoUtils {
49 private static final String TAG = "ContactPhotoUtils";
50
51 private static final String PHOTO_DATE_FORMAT = "'IMG'_yyyyMMdd_HHmmss";
52
Josh Garguse5d3f892012-04-11 11:56:15 -070053 /**
54 * Generate a new, unique file to be used as an out-of-band communication
55 * channel, since hi-res Bitmaps are too big to serialize into a Bundle.
Yorke Lee637a38e2013-09-14 08:36:33 -070056 * This file will be passed (as a uri) to other activities (such as the gallery/camera/
57 * cropper/etc.), and read by us once they are finished writing it.
Josh Garguse5d3f892012-04-11 11:56:15 -070058 */
Yorke Lee637a38e2013-09-14 08:36:33 -070059 public static Uri generateTempImageUri(Context context) {
Paul Soulosfc98ff92014-09-12 10:45:43 -070060 final String fileProviderAuthority = context.getResources().getString(
61 R.string.photo_file_provider_authority);
62 return FileProvider.getUriForFile(context, fileProviderAuthority,
Yorke Lee637a38e2013-09-14 08:36:33 -070063 new File(pathForTempPhoto(context, generateTempPhotoFileName())));
Josh Garguse5d3f892012-04-11 11:56:15 -070064 }
65
Yorke Lee637a38e2013-09-14 08:36:33 -070066 public static Uri generateTempCroppedImageUri(Context context) {
Paul Soulosfc98ff92014-09-12 10:45:43 -070067 final String fileProviderAuthority = context.getResources().getString(
68 R.string.photo_file_provider_authority);
69 return FileProvider.getUriForFile(context, fileProviderAuthority,
Yorke Lee637a38e2013-09-14 08:36:33 -070070 new File(pathForTempPhoto(context, generateTempCroppedPhotoFileName())));
71 }
72
73 private static String pathForTempPhoto(Context context, String fileName) {
74 final File dir = context.getCacheDir();
Josh Gargusebc17922012-05-04 18:47:09 -070075 dir.mkdirs();
76 final File f = new File(dir, fileName);
77 return f.getAbsolutePath();
78 }
79
Yorke Lee637a38e2013-09-14 08:36:33 -070080 private static String generateTempPhotoFileName() {
81 final Date date = new Date(System.currentTimeMillis());
Johan Redestig9bcfc322012-10-05 08:23:51 +020082 SimpleDateFormat dateFormat = new SimpleDateFormat(PHOTO_DATE_FORMAT, Locale.US);
Josh Gargusebc17922012-05-04 18:47:09 -070083 return "ContactPhoto-" + dateFormat.format(date) + ".jpg";
Josh Garguse5d3f892012-04-11 11:56:15 -070084 }
85
Yorke Lee637a38e2013-09-14 08:36:33 -070086 private static String generateTempCroppedPhotoFileName() {
87 final Date date = new Date(System.currentTimeMillis());
88 SimpleDateFormat dateFormat = new SimpleDateFormat(PHOTO_DATE_FORMAT, Locale.US);
89 return "ContactPhoto-" + dateFormat.format(date) + "-cropped.jpg";
90 }
91
92 /**
93 * Given a uri pointing to a bitmap, reads it into a bitmap and returns it.
94 * @throws FileNotFoundException
95 */
96 public static Bitmap getBitmapFromUri(Context context, Uri uri) throws FileNotFoundException {
97 final InputStream imageStream = context.getContentResolver().openInputStream(uri);
98 try {
99 return BitmapFactory.decodeStream(imageStream);
100 } finally {
101 Closeables.closeQuietly(imageStream);
102 }
103 }
104
Josh Garguse5d3f892012-04-11 11:56:15 -0700105 /**
106 * Creates a byte[] containing the PNG-compressed bitmap, or null if
107 * something goes wrong.
108 */
109 public static byte[] compressBitmap(Bitmap bitmap) {
110 final int size = bitmap.getWidth() * bitmap.getHeight() * 4;
111 final ByteArrayOutputStream out = new ByteArrayOutputStream(size);
112 try {
113 bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
114 out.flush();
115 out.close();
116 return out.toByteArray();
117 } catch (IOException e) {
118 Log.w(TAG, "Unable to serialize photo: " + e.toString());
119 return null;
120 }
121 }
Chiao Cheng61414c22012-06-18 16:59:06 -0700122
Yorke Lee637a38e2013-09-14 08:36:33 -0700123 public static void addCropExtras(Intent intent, int photoSize) {
Chiao Cheng61414c22012-06-18 16:59:06 -0700124 intent.putExtra("crop", "true");
125 intent.putExtra("scale", true);
126 intent.putExtra("scaleUpIfNeeded", true);
127 intent.putExtra("aspectX", 1);
128 intent.putExtra("aspectY", 1);
129 intent.putExtra("outputX", photoSize);
130 intent.putExtra("outputY", photoSize);
Yorke Lee637a38e2013-09-14 08:36:33 -0700131 }
132
133 /**
134 * Adds common extras to gallery intents.
135 *
136 * @param intent The intent to add extras to.
137 * @param photoUri The uri of the file to save the image to.
138 */
139 public static void addPhotoPickerExtras(Intent intent, Uri photoUri) {
140 intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
141 intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
142 Intent.FLAG_GRANT_READ_URI_PERMISSION);
143 intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, photoUri));
144 }
145
146 /**
147 * Given an input photo stored in a uri, save it to a destination uri
148 */
149 public static boolean savePhotoFromUriToUri(Context context, Uri inputUri, Uri outputUri,
150 boolean deleteAfterSave) {
Jay Shraunera9433712015-02-27 15:47:09 -0800151 if (inputUri == null || outputUri == null) {
152 return false;
153 }
Paul Duffinf43973f2015-05-27 13:33:10 +0100154 try (FileOutputStream outputStream = context.getContentResolver()
155 .openAssetFileDescriptor(outputUri, "rw").createOutputStream();
156 InputStream inputStream = context.getContentResolver().openInputStream(inputUri)) {
Yorke Lee637a38e2013-09-14 08:36:33 -0700157
158 final byte[] buffer = new byte[16 * 1024];
159 int length;
160 int totalLength = 0;
161 while ((length = inputStream.read(buffer)) > 0) {
162 outputStream.write(buffer, 0, length);
163 totalLength += length;
164 }
165 Log.v(TAG, "Wrote " + totalLength + " bytes for photo " + inputUri.toString());
Jay Shraunerb716f502015-01-14 16:11:08 -0800166 } catch (IOException | NullPointerException e) {
Yorke Lee637a38e2013-09-14 08:36:33 -0700167 Log.e(TAG, "Failed to write photo: " + inputUri.toString() + " because: " + e);
168 return false;
169 } finally {
Yorke Lee637a38e2013-09-14 08:36:33 -0700170 if (deleteAfterSave) {
171 context.getContentResolver().delete(inputUri, null, null);
172 }
173 }
174 return true;
Chiao Cheng61414c22012-06-18 16:59:06 -0700175 }
Josh Garguse5d3f892012-04-11 11:56:15 -0700176}