238 lines
8.6 KiB
Java
238 lines
8.6 KiB
Java
/*
|
|
* Copyright (C) 2009 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.soundcloud.android.crop;
|
|
|
|
import android.app.ProgressDialog;
|
|
import android.content.ContentResolver;
|
|
import android.content.Context;
|
|
import android.database.Cursor;
|
|
import android.media.ExifInterface;
|
|
import android.net.Uri;
|
|
import android.os.Handler;
|
|
import android.os.ParcelFileDescriptor;
|
|
import android.provider.MediaStore;
|
|
import androidx.annotation.Nullable;
|
|
import android.text.TextUtils;
|
|
import android.util.Pair;
|
|
|
|
import java.io.Closeable;
|
|
import java.io.File;
|
|
import java.io.FileDescriptor;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
|
|
/*
|
|
* Modified from original in AOSP.
|
|
*/
|
|
class CropUtil {
|
|
|
|
private static final String SCHEME_FILE = "file";
|
|
private static final String SCHEME_CONTENT = "content";
|
|
|
|
public static void closeSilently(@Nullable Closeable c) {
|
|
if (c == null) return;
|
|
try {
|
|
c.close();
|
|
} catch (Throwable t) {
|
|
// Do nothing
|
|
}
|
|
}
|
|
|
|
public static Pair<Integer,Integer> getExifRotationTranslation(File imageFile) {
|
|
int exifRotation = 0, exifScale = 1;
|
|
if (imageFile == null) return new Pair<Integer,Integer>(0,1);
|
|
try {
|
|
ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
|
|
switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
|
|
case ExifInterface.ORIENTATION_UNDEFINED:
|
|
case ExifInterface.ORIENTATION_NORMAL:
|
|
break ;
|
|
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
|
|
exifScale=-1;
|
|
break;
|
|
case ExifInterface.ORIENTATION_ROTATE_180:
|
|
exifRotation = 180;
|
|
break;
|
|
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
|
|
exifRotation = 180;
|
|
exifScale=-1;
|
|
break;
|
|
case ExifInterface.ORIENTATION_TRANSPOSE:
|
|
exifRotation = 90;
|
|
exifScale=-1;
|
|
break;
|
|
case ExifInterface.ORIENTATION_ROTATE_90:
|
|
exifRotation = 90;
|
|
break;
|
|
case ExifInterface.ORIENTATION_TRANSVERSE:
|
|
exifRotation = 270;
|
|
exifScale=-1;
|
|
break;
|
|
case ExifInterface.ORIENTATION_ROTATE_270:
|
|
exifRotation = 270;
|
|
}
|
|
} catch (IOException e) {
|
|
Log.e("Error getting Exif data", e);
|
|
}
|
|
return new Pair<Integer,Integer>(exifRotation,exifScale);
|
|
}
|
|
|
|
public static boolean copyExifRotation(File sourceFile, File destFile) {
|
|
if (sourceFile == null || destFile == null) return false;
|
|
try {
|
|
ExifInterface exifSource = new ExifInterface(sourceFile.getAbsolutePath());
|
|
ExifInterface exifDest = new ExifInterface(destFile.getAbsolutePath());
|
|
exifDest.setAttribute(ExifInterface.TAG_ORIENTATION, exifSource.getAttribute(ExifInterface.TAG_ORIENTATION));
|
|
exifDest.saveAttributes();
|
|
return true;
|
|
} catch (IOException e) {
|
|
Log.e("Error copying Exif data", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public static File getFromMediaUri(Context context, ContentResolver resolver, Uri uri) {
|
|
if (uri == null) return null;
|
|
|
|
if (SCHEME_FILE.equals(uri.getScheme())) {
|
|
return new File(uri.getPath());
|
|
} else if (SCHEME_CONTENT.equals(uri.getScheme())) {
|
|
final String[] filePathColumn = { MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME };
|
|
Cursor cursor = null;
|
|
try {
|
|
cursor = resolver.query(uri, filePathColumn, null, null, null);
|
|
if (cursor != null && cursor.moveToFirst()) {
|
|
final int columnIndex = (uri.toString().startsWith("content://com.google.android.gallery3d")) ?
|
|
cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME) :
|
|
cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
|
|
// Picasa images on API 13+
|
|
if (columnIndex != -1) {
|
|
String filePath = cursor.getString(columnIndex);
|
|
if (!TextUtils.isEmpty(filePath)) {
|
|
return new File(filePath);
|
|
}
|
|
}
|
|
}
|
|
} catch (IllegalArgumentException e) {
|
|
// Google Drive images
|
|
return getFromMediaUriPfd(context, resolver, uri);
|
|
} catch (SecurityException ignored) {
|
|
// Nothing we can do
|
|
} finally {
|
|
if (cursor != null) cursor.close();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static String getTempFilename(Context context) throws IOException {
|
|
File outputDir = context.getCacheDir();
|
|
File outputFile = File.createTempFile("image", "tmp", outputDir);
|
|
return outputFile.getAbsolutePath();
|
|
}
|
|
|
|
@Nullable
|
|
private static File getFromMediaUriPfd(Context context, ContentResolver resolver, Uri uri) {
|
|
if (uri == null) return null;
|
|
|
|
FileInputStream input = null;
|
|
FileOutputStream output = null;
|
|
try {
|
|
ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r");
|
|
FileDescriptor fd = pfd.getFileDescriptor();
|
|
input = new FileInputStream(fd);
|
|
|
|
String tempFilename = getTempFilename(context);
|
|
output = new FileOutputStream(tempFilename);
|
|
|
|
int read;
|
|
byte[] bytes = new byte[4096];
|
|
while ((read = input.read(bytes)) != -1) {
|
|
output.write(bytes, 0, read);
|
|
}
|
|
return new File(tempFilename);
|
|
} catch (IOException ignored) {
|
|
// Nothing we can do
|
|
} finally {
|
|
closeSilently(input);
|
|
closeSilently(output);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static void startBackgroundJob(MonitoredActivity activity,
|
|
String title, String message, Runnable job, Handler handler) {
|
|
// Make the progress dialog uncancelable, so that we can guarantee
|
|
// the thread will be done before the activity getting destroyed
|
|
ProgressDialog dialog = ProgressDialog.show(
|
|
activity, title, message, true, false);
|
|
new Thread(new BackgroundJob(activity, job, dialog, handler)).start();
|
|
}
|
|
|
|
private static class BackgroundJob extends MonitoredActivity.LifeCycleAdapter implements Runnable {
|
|
|
|
private final MonitoredActivity activity;
|
|
private final ProgressDialog dialog;
|
|
private final Runnable job;
|
|
private final Handler handler;
|
|
private final Runnable cleanupRunner = new Runnable() {
|
|
public void run() {
|
|
activity.removeLifeCycleListener(BackgroundJob.this);
|
|
if (dialog.getWindow() != null) dialog.dismiss();
|
|
}
|
|
};
|
|
|
|
public BackgroundJob(MonitoredActivity activity, Runnable job,
|
|
ProgressDialog dialog, Handler handler) {
|
|
this.activity = activity;
|
|
this.dialog = dialog;
|
|
this.job = job;
|
|
this.activity.addLifeCycleListener(this);
|
|
this.handler = handler;
|
|
}
|
|
|
|
public void run() {
|
|
try {
|
|
job.run();
|
|
} finally {
|
|
handler.post(cleanupRunner);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onActivityDestroyed(MonitoredActivity activity) {
|
|
// We get here only when the onDestroyed being called before
|
|
// the cleanupRunner. So, run it now and remove it from the queue
|
|
cleanupRunner.run();
|
|
handler.removeCallbacks(cleanupRunner);
|
|
}
|
|
|
|
@Override
|
|
public void onActivityStopped(MonitoredActivity activity) {
|
|
dialog.hide();
|
|
}
|
|
|
|
@Override
|
|
public void onActivityStarted(MonitoredActivity activity) {
|
|
dialog.show();
|
|
}
|
|
}
|
|
|
|
}
|