/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.contacts.util; import android.content.ClipData; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; import android.support.v4.content.FileProvider; import android.util.Log; import com.android.contacts.R; import com.google.common.io.Closeables; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; /** * Utilities related to loading/saving contact photos. * */ public class ContactPhotoUtils { private static final String TAG = "ContactPhotoUtils"; private static final String PHOTO_DATE_FORMAT = "'IMG'_yyyyMMdd_HHmmss"; /** * Generate a new, unique file to be used as an out-of-band communication * channel, since hi-res Bitmaps are too big to serialize into a Bundle. * This file will be passed (as a uri) to other activities (such as the gallery/camera/ * cropper/etc.), and read by us once they are finished writing it. */ public static Uri generateTempImageUri(Context context) { final String fileProviderAuthority = context.getResources().getString( R.string.photo_file_provider_authority); return FileProvider.getUriForFile(context, fileProviderAuthority, new File(pathForTempPhoto(context, generateTempPhotoFileName()))); } public static Uri generateTempCroppedImageUri(Context context) { final String fileProviderAuthority = context.getResources().getString( R.string.photo_file_provider_authority); return FileProvider.getUriForFile(context, fileProviderAuthority, new File(pathForTempPhoto(context, generateTempCroppedPhotoFileName()))); } private static String pathForTempPhoto(Context context, String fileName) { final File dir = context.getCacheDir(); dir.mkdirs(); final File f = new File(dir, fileName); return f.getAbsolutePath(); } private static String generateTempPhotoFileName() { final Date date = new Date(System.currentTimeMillis()); SimpleDateFormat dateFormat = new SimpleDateFormat(PHOTO_DATE_FORMAT, Locale.US); return "ContactPhoto-" + dateFormat.format(date) + ".jpg"; } private static String generateTempCroppedPhotoFileName() { final Date date = new Date(System.currentTimeMillis()); SimpleDateFormat dateFormat = new SimpleDateFormat(PHOTO_DATE_FORMAT, Locale.US); return "ContactPhoto-" + dateFormat.format(date) + "-cropped.jpg"; } /** * Given a uri pointing to a bitmap, reads it into a bitmap and returns it. * @throws FileNotFoundException */ public static Bitmap getBitmapFromUri(Context context, Uri uri) throws FileNotFoundException { final InputStream imageStream = context.getContentResolver().openInputStream(uri); try { return BitmapFactory.decodeStream(imageStream); } finally { Closeables.closeQuietly(imageStream); } } /** * Creates a byte[] containing the PNG-compressed bitmap, or null if * something goes wrong. */ public static byte[] compressBitmap(Bitmap bitmap) { final int size = bitmap.getWidth() * bitmap.getHeight() * 4; final ByteArrayOutputStream out = new ByteArrayOutputStream(size); try { bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); out.flush(); out.close(); return out.toByteArray(); } catch (IOException e) { Log.w(TAG, "Unable to serialize photo: " + e.toString()); return null; } } public static void addCropExtras(Intent intent, int photoSize) { intent.putExtra("crop", "true"); intent.putExtra("scale", true); intent.putExtra("scaleUpIfNeeded", true); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("outputX", photoSize); intent.putExtra("outputY", photoSize); } /** * Adds common extras to gallery intents. * * @param intent The intent to add extras to. * @param photoUri The uri of the file to save the image to. */ public static void addPhotoPickerExtras(Intent intent, Uri photoUri) { intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, photoUri)); } /** * Given an input photo stored in a uri, save it to a destination uri */ public static boolean savePhotoFromUriToUri(Context context, Uri inputUri, Uri outputUri, boolean deleteAfterSave) { if (inputUri == null || outputUri == null) { return false; } try (FileOutputStream outputStream = context.getContentResolver() .openAssetFileDescriptor(outputUri, "rw").createOutputStream(); InputStream inputStream = context.getContentResolver().openInputStream(inputUri)) { final byte[] buffer = new byte[16 * 1024]; int length; int totalLength = 0; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); totalLength += length; } Log.v(TAG, "Wrote " + totalLength + " bytes for photo " + inputUri.toString()); } catch (IOException | NullPointerException e) { Log.e(TAG, "Failed to write photo: " + inputUri.toString() + " because: " + e); return false; } finally { if (deleteAfterSave) { context.getContentResolver().delete(inputUri, null, null); } } return true; } }