1e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus/* 2e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * Copyright (C) 2012 The Android Open Source Project 3e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * 4e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * Licensed under the Apache License, Version 2.0 (the "License"); 5e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * you may not use this file except in compliance with the License. 6e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * You may obtain a copy of the License at 7e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * 8e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * http://www.apache.org/licenses/LICENSE-2.0 9e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * 10e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * Unless required by applicable law or agreed to in writing, software 11e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * distributed under the License is distributed on an "AS IS" BASIS, 12e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * See the License for the specific language governing permissions and 14e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * limitations under the License. 15e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus */ 16e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 17e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 18e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Garguspackage com.android.contacts.util; 19e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 20637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Leeimport android.content.ClipData; 21ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargusimport android.content.Context; 2261414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Chengimport android.content.Intent; 23e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargusimport android.graphics.Bitmap; 24637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Leeimport android.graphics.BitmapFactory; 2561414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Chengimport android.net.Uri; 26e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargusimport android.os.Environment; 2761414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Chengimport android.provider.MediaStore; 28637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Leeimport android.support.v4.content.FileProvider; 29e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargusimport android.util.Log; 30e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 31fc98ff91106de05bef5b59c3a21ed4ac724847fePaul Soulosimport com.android.contacts.R; 32637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Leeimport com.google.common.io.Closeables; 33637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee 34e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargusimport java.io.ByteArrayOutputStream; 35e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargusimport java.io.File; 36637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Leeimport java.io.FileNotFoundException; 37637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Leeimport java.io.FileOutputStream; 38e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargusimport java.io.IOException; 39637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Leeimport java.io.InputStream; 40e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargusimport java.text.SimpleDateFormat; 41e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargusimport java.util.Date; 429bcfc325333797ad968fbb42583a2006effdef4aJohan Redestigimport java.util.Locale; 43e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 44e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus/** 45e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * Utilities related to loading/saving contact photos. 46e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * 47e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus */ 48e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Garguspublic class ContactPhotoUtils { 49e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus private static final String TAG = "ContactPhotoUtils"; 50e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 51e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus private static final String PHOTO_DATE_FORMAT = "'IMG'_yyyyMMdd_HHmmss"; 52e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 53e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus /** 54e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * Generate a new, unique file to be used as an out-of-band communication 55e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * channel, since hi-res Bitmaps are too big to serialize into a Bundle. 56637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * This file will be passed (as a uri) to other activities (such as the gallery/camera/ 57637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * cropper/etc.), and read by us once they are finished writing it. 58e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus */ 59637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee public static Uri generateTempImageUri(Context context) { 60fc98ff91106de05bef5b59c3a21ed4ac724847fePaul Soulos final String fileProviderAuthority = context.getResources().getString( 61fc98ff91106de05bef5b59c3a21ed4ac724847fePaul Soulos R.string.photo_file_provider_authority); 62fc98ff91106de05bef5b59c3a21ed4ac724847fePaul Soulos return FileProvider.getUriForFile(context, fileProviderAuthority, 63637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee new File(pathForTempPhoto(context, generateTempPhotoFileName()))); 64e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus } 65e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 66637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee public static Uri generateTempCroppedImageUri(Context context) { 67fc98ff91106de05bef5b59c3a21ed4ac724847fePaul Soulos final String fileProviderAuthority = context.getResources().getString( 68fc98ff91106de05bef5b59c3a21ed4ac724847fePaul Soulos R.string.photo_file_provider_authority); 69fc98ff91106de05bef5b59c3a21ed4ac724847fePaul Soulos return FileProvider.getUriForFile(context, fileProviderAuthority, 70637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee new File(pathForTempPhoto(context, generateTempCroppedPhotoFileName()))); 71ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus } 72ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus 73637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private static String pathForTempPhoto(Context context, String fileName) { 74637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final File dir = context.getCacheDir(); 75ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus dir.mkdirs(); 76ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final File f = new File(dir, fileName); 77ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus return f.getAbsolutePath(); 78ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus } 79ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus 80637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private static String generateTempPhotoFileName() { 81637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final Date date = new Date(System.currentTimeMillis()); 829bcfc325333797ad968fbb42583a2006effdef4aJohan Redestig SimpleDateFormat dateFormat = new SimpleDateFormat(PHOTO_DATE_FORMAT, Locale.US); 83ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus return "ContactPhoto-" + dateFormat.format(date) + ".jpg"; 84e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus } 85e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus 86637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee private static String generateTempCroppedPhotoFileName() { 87637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final Date date = new Date(System.currentTimeMillis()); 88637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee SimpleDateFormat dateFormat = new SimpleDateFormat(PHOTO_DATE_FORMAT, Locale.US); 89637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee return "ContactPhoto-" + dateFormat.format(date) + "-cropped.jpg"; 90637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 91637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee 92637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee /** 93637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * Given a uri pointing to a bitmap, reads it into a bitmap and returns it. 94637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * @throws FileNotFoundException 95637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee */ 96637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee public static Bitmap getBitmapFromUri(Context context, Uri uri) throws FileNotFoundException { 97637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final InputStream imageStream = context.getContentResolver().openInputStream(uri); 98637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee try { 99637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee return BitmapFactory.decodeStream(imageStream); 100637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } finally { 101637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee Closeables.closeQuietly(imageStream); 102637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 103637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 104637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee 105e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus /** 106e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * Creates a byte[] containing the PNG-compressed bitmap, or null if 107e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus * something goes wrong. 108e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus */ 109e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus public static byte[] compressBitmap(Bitmap bitmap) { 110e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus final int size = bitmap.getWidth() * bitmap.getHeight() * 4; 111e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus final ByteArrayOutputStream out = new ByteArrayOutputStream(size); 112e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus try { 113e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); 114e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus out.flush(); 115e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus out.close(); 116e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus return out.toByteArray(); 117e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus } catch (IOException e) { 118e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus Log.w(TAG, "Unable to serialize photo: " + e.toString()); 119e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus return null; 120e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus } 121e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus } 12261414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng 123637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee public static void addCropExtras(Intent intent, int photoSize) { 12461414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng intent.putExtra("crop", "true"); 12561414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng intent.putExtra("scale", true); 12661414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng intent.putExtra("scaleUpIfNeeded", true); 12761414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng intent.putExtra("aspectX", 1); 12861414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng intent.putExtra("aspectY", 1); 12961414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng intent.putExtra("outputX", photoSize); 13061414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng intent.putExtra("outputY", photoSize); 131637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 132637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee 133637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee /** 134637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * Adds common extras to gallery intents. 135637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * 136637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * @param intent The intent to add extras to. 137637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * @param photoUri The uri of the file to save the image to. 138637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee */ 139637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee public static void addPhotoPickerExtras(Intent intent, Uri photoUri) { 140637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); 141637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | 142637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee Intent.FLAG_GRANT_READ_URI_PERMISSION); 143637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, photoUri)); 144637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 145637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee 146637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee /** 147637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee * Given an input photo stored in a uri, save it to a destination uri 148637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee */ 149637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee public static boolean savePhotoFromUriToUri(Context context, Uri inputUri, Uri outputUri, 150637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee boolean deleteAfterSave) { 151a9433713594c990cd3b0ac5a44e316e4b54992b0Jay Shrauner if (inputUri == null || outputUri == null) { 152a9433713594c990cd3b0ac5a44e316e4b54992b0Jay Shrauner return false; 153a9433713594c990cd3b0ac5a44e316e4b54992b0Jay Shrauner } 154f43973fd13262553b497cd83f4ea9654a72ff22fPaul Duffin try (FileOutputStream outputStream = context.getContentResolver() 155f43973fd13262553b497cd83f4ea9654a72ff22fPaul Duffin .openAssetFileDescriptor(outputUri, "rw").createOutputStream(); 156f43973fd13262553b497cd83f4ea9654a72ff22fPaul Duffin InputStream inputStream = context.getContentResolver().openInputStream(inputUri)) { 157637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee 158637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee final byte[] buffer = new byte[16 * 1024]; 159637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee int length; 160637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee int totalLength = 0; 161637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee while ((length = inputStream.read(buffer)) > 0) { 162637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee outputStream.write(buffer, 0, length); 163637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee totalLength += length; 164637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 165637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee Log.v(TAG, "Wrote " + totalLength + " bytes for photo " + inputUri.toString()); 166b716f50c6f30b347b403db8b39598c38aadd8c64Jay Shrauner } catch (IOException | NullPointerException e) { 167637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee Log.e(TAG, "Failed to write photo: " + inputUri.toString() + " because: " + e); 168637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee return false; 169637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } finally { 170637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee if (deleteAfterSave) { 171637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee context.getContentResolver().delete(inputUri, null, null); 172637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 173637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee } 174637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee return true; 17561414c2c4627ab588cc0f54eae5e0c5591f7d8d9Chiao Cheng } 176e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus} 177