2012-07-13 11:06:24 -07:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
package org.mozilla.gecko;
|
|
|
|
|
2014-07-21 10:19:41 -07:00
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
2012-08-02 10:33:44 -07:00
|
|
|
import org.mozilla.gecko.util.ActivityResultHandler;
|
2014-03-06 12:23:53 -08:00
|
|
|
import org.mozilla.gecko.util.ThreadUtils;
|
2012-08-02 10:33:44 -07:00
|
|
|
|
2012-07-13 11:06:24 -07:00
|
|
|
import android.app.Activity;
|
|
|
|
import android.content.ContentResolver;
|
2014-03-06 12:23:52 -08:00
|
|
|
import android.content.Context;
|
2012-07-13 11:06:24 -07:00
|
|
|
import android.content.Intent;
|
|
|
|
import android.database.Cursor;
|
|
|
|
import android.net.Uri;
|
2014-02-13 08:17:00 -08:00
|
|
|
import android.os.Bundle;
|
|
|
|
import android.os.Environment;
|
2014-07-21 10:19:41 -07:00
|
|
|
import android.os.Process;
|
2014-02-13 08:17:00 -08:00
|
|
|
import android.provider.MediaStore;
|
2012-07-13 11:06:24 -07:00
|
|
|
import android.provider.OpenableColumns;
|
2014-02-13 08:17:00 -08:00
|
|
|
import android.support.v4.app.FragmentActivity;
|
|
|
|
import android.support.v4.app.LoaderManager;
|
|
|
|
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
|
|
|
import android.support.v4.content.CursorLoader;
|
|
|
|
import android.support.v4.content.Loader;
|
2014-03-06 12:23:53 -08:00
|
|
|
import android.text.TextUtils;
|
2014-02-13 08:17:00 -08:00
|
|
|
import android.text.format.Time;
|
2012-07-13 11:06:24 -07:00
|
|
|
import android.util.Log;
|
2012-07-27 17:53:54 -07:00
|
|
|
|
2014-02-13 08:17:00 -08:00
|
|
|
class FilePickerResultHandler implements ActivityResultHandler {
|
2012-07-13 11:06:24 -07:00
|
|
|
private static final String LOGTAG = "GeckoFilePickerResultHandler";
|
2014-03-06 12:23:52 -08:00
|
|
|
private static final String UPLOADS_DIR = "uploads";
|
2012-07-13 11:06:24 -07:00
|
|
|
|
2014-07-21 10:19:41 -07:00
|
|
|
private final FilePicker.ResultHandler handler;
|
2014-03-06 12:23:53 -08:00
|
|
|
private final int tabId;
|
2014-03-06 12:23:52 -08:00
|
|
|
private final File cacheDir;
|
2012-07-13 11:06:24 -07:00
|
|
|
|
2014-02-13 08:17:00 -08:00
|
|
|
// this code is really hacky and doesn't belong anywhere so I'm putting it here for now
|
|
|
|
// until I can come up with a better solution.
|
|
|
|
private String mImageName = "";
|
|
|
|
|
|
|
|
/* Use this constructor to asynchronously listen for results */
|
2014-03-06 12:23:53 -08:00
|
|
|
public FilePickerResultHandler(final FilePicker.ResultHandler handler, final Context context, final int tabId) {
|
|
|
|
this.tabId = tabId;
|
2014-07-21 10:19:41 -07:00
|
|
|
this.cacheDir = new File(context.getCacheDir(), UPLOADS_DIR);
|
2014-03-06 12:23:52 -08:00
|
|
|
this.handler = handler;
|
2012-07-13 11:06:24 -07:00
|
|
|
}
|
|
|
|
|
2014-07-21 10:19:41 -07:00
|
|
|
void sendResult(String res) {
|
2014-03-06 12:23:52 -08:00
|
|
|
if (handler != null) {
|
|
|
|
handler.gotFile(res);
|
|
|
|
}
|
2014-02-13 08:17:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onActivityResult(int resultCode, Intent intent) {
|
|
|
|
if (resultCode != Activity.RESULT_OK) {
|
|
|
|
sendResult("");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Camera results won't return an Intent. Use the file name we passed to the original intent.
|
|
|
|
if (intent == null) {
|
|
|
|
if (mImageName != null) {
|
|
|
|
File file = new File(Environment.getExternalStorageDirectory(), mImageName);
|
|
|
|
sendResult(file.getAbsolutePath());
|
|
|
|
} else {
|
|
|
|
sendResult("");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Uri uri = intent.getData();
|
|
|
|
if (uri == null) {
|
|
|
|
sendResult("");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some file pickers may return a file uri
|
2012-07-13 11:06:24 -07:00
|
|
|
if ("file".equals(uri.getScheme())) {
|
|
|
|
String path = uri.getPath();
|
2014-02-13 08:17:00 -08:00
|
|
|
sendResult(path == null ? "" : path);
|
|
|
|
return;
|
2012-07-13 11:06:24 -07:00
|
|
|
}
|
2014-02-13 08:17:00 -08:00
|
|
|
|
|
|
|
final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
|
|
|
|
final LoaderManager lm = fa.getSupportLoaderManager();
|
|
|
|
// Finally, Video pickers and some file pickers may return a content provider.
|
2014-03-04 08:29:26 -08:00
|
|
|
Cursor cursor = null;
|
2012-07-13 11:06:24 -07:00
|
|
|
try {
|
2014-02-13 08:17:00 -08:00
|
|
|
// Try a query to make sure the expected columns exist
|
|
|
|
final ContentResolver cr = fa.getContentResolver();
|
2014-03-04 08:29:26 -08:00
|
|
|
cursor = cr.query(uri, new String[] { MediaStore.Video.Media.DATA }, null, null, null);
|
2014-02-13 08:17:00 -08:00
|
|
|
|
2014-03-04 08:29:26 -08:00
|
|
|
int index = cursor.getColumnIndex(MediaStore.Video.Media.DATA);
|
|
|
|
if (index >= 0) {
|
|
|
|
lm.initLoader(intent.hashCode(), null, new VideoLoaderCallbacks(uri));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} catch(Exception ex) {
|
|
|
|
// We'll try a different loader below
|
|
|
|
} finally {
|
|
|
|
if (cursor != null) {
|
|
|
|
cursor.close();
|
|
|
|
}
|
|
|
|
}
|
2014-02-13 08:17:00 -08:00
|
|
|
|
2014-07-21 10:19:41 -07:00
|
|
|
lm.initLoader(uri.hashCode(), null, new FileLoaderCallbacks(uri, cacheDir, tabId));
|
2014-02-13 08:17:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public String generateImageName() {
|
|
|
|
Time now = new Time();
|
|
|
|
now.setToNow();
|
|
|
|
mImageName = now.format("%Y-%m-%d %H.%M.%S") + ".jpg";
|
|
|
|
return mImageName;
|
|
|
|
}
|
|
|
|
|
|
|
|
private class VideoLoaderCallbacks implements LoaderCallbacks<Cursor> {
|
2014-03-06 12:23:53 -08:00
|
|
|
final private Uri uri;
|
2014-02-13 08:17:00 -08:00
|
|
|
public VideoLoaderCallbacks(Uri uri) {
|
2014-03-06 12:23:53 -08:00
|
|
|
this.uri = uri;
|
2014-02-13 08:17:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
|
|
|
final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
|
|
|
|
return new CursorLoader(fa,
|
2014-03-06 12:23:53 -08:00
|
|
|
uri,
|
2014-03-04 08:29:26 -08:00
|
|
|
new String[] { MediaStore.Video.Media.DATA },
|
2014-02-13 08:17:00 -08:00
|
|
|
null, // selection
|
|
|
|
null, // selectionArgs
|
|
|
|
null); // sortOrder
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
|
|
|
if (cursor.moveToFirst()) {
|
|
|
|
String res = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA));
|
2014-05-19 15:49:09 -07:00
|
|
|
|
|
|
|
// Some pickers (the KitKat Documents one for instance) won't return a temporary file here.
|
|
|
|
// Fall back to the normal FileLoader if we didn't find anything.
|
|
|
|
if (TextUtils.isEmpty(res)) {
|
|
|
|
tryFileLoaderCallback();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-13 08:17:00 -08:00
|
|
|
sendResult(res);
|
2014-05-19 15:49:09 -07:00
|
|
|
} else {
|
|
|
|
tryFileLoaderCallback();
|
2014-02-13 08:17:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-19 15:49:09 -07:00
|
|
|
private void tryFileLoaderCallback() {
|
|
|
|
final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
|
|
|
|
final LoaderManager lm = fa.getSupportLoaderManager();
|
2014-07-21 10:19:41 -07:00
|
|
|
lm.initLoader(uri.hashCode(), null, new FileLoaderCallbacks(uri, cacheDir, tabId));
|
2014-05-19 15:49:09 -07:00
|
|
|
}
|
|
|
|
|
2014-02-13 08:17:00 -08:00
|
|
|
@Override
|
|
|
|
public void onLoaderReset(Loader<Cursor> loader) { }
|
|
|
|
}
|
|
|
|
|
2014-07-21 10:19:41 -07:00
|
|
|
/**
|
|
|
|
* This class's only dependency on FilePickerResultHandler is sendResult.
|
|
|
|
*/
|
2014-03-06 12:23:53 -08:00
|
|
|
private class FileLoaderCallbacks implements LoaderCallbacks<Cursor>,
|
|
|
|
Tabs.OnTabsChangedListener {
|
2014-07-21 10:19:41 -07:00
|
|
|
private final Uri uri;
|
|
|
|
private final File cacheDir;
|
|
|
|
private final int tabId;
|
|
|
|
/* inner-access */ String tempFile;
|
2014-03-06 12:23:53 -08:00
|
|
|
|
2014-07-21 10:19:41 -07:00
|
|
|
public FileLoaderCallbacks(Uri uri, File cacheDir, int tabId) {
|
2014-03-06 12:23:53 -08:00
|
|
|
this.uri = uri;
|
2014-07-21 10:19:41 -07:00
|
|
|
this.cacheDir = cacheDir;
|
|
|
|
this.tabId = tabId;
|
2014-02-13 08:17:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
|
|
|
final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
|
|
|
|
return new CursorLoader(fa,
|
2014-03-06 12:23:53 -08:00
|
|
|
uri,
|
2014-02-13 08:17:00 -08:00
|
|
|
new String[] { OpenableColumns.DISPLAY_NAME },
|
|
|
|
null, // selection
|
|
|
|
null, // selectionArgs
|
|
|
|
null); // sortOrder
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
|
|
|
if (cursor.moveToFirst()) {
|
|
|
|
String name = cursor.getString(0);
|
|
|
|
// tmp filenames must be at least 3 characters long. Add a prefix to make sure that happens
|
2014-03-06 12:23:53 -08:00
|
|
|
String fileName = "tmp_" + Process.myPid() + "-";
|
2014-07-21 10:19:41 -07:00
|
|
|
String fileExt;
|
2014-02-13 08:17:00 -08:00
|
|
|
int period;
|
|
|
|
|
|
|
|
final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
|
|
|
|
final ContentResolver cr = fa.getContentResolver();
|
|
|
|
|
|
|
|
// Generate an extension if we don't already have one
|
|
|
|
if (name == null || (period = name.lastIndexOf('.')) == -1) {
|
2014-03-06 12:23:53 -08:00
|
|
|
String mimeType = cr.getType(uri);
|
2014-02-13 08:17:00 -08:00
|
|
|
fileExt = "." + GeckoAppShell.getExtensionFromMimeType(mimeType);
|
|
|
|
} else {
|
|
|
|
fileExt = name.substring(period);
|
|
|
|
fileName += name.substring(0, period);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now write the data to the temp file
|
2012-07-13 11:06:24 -07:00
|
|
|
try {
|
2014-03-06 12:23:52 -08:00
|
|
|
cacheDir.mkdir();
|
|
|
|
|
|
|
|
File file = File.createTempFile(fileName, fileExt, cacheDir);
|
2014-02-13 08:17:00 -08:00
|
|
|
FileOutputStream fos = new FileOutputStream(file);
|
2014-03-06 12:23:53 -08:00
|
|
|
InputStream is = cr.openInputStream(uri);
|
2014-02-13 08:17:00 -08:00
|
|
|
byte[] buf = new byte[4096];
|
|
|
|
int len = is.read(buf);
|
|
|
|
while (len != -1) {
|
|
|
|
fos.write(buf, 0, len);
|
|
|
|
len = is.read(buf);
|
2012-07-13 11:06:24 -07:00
|
|
|
}
|
2014-02-13 08:17:00 -08:00
|
|
|
fos.close();
|
2014-02-14 09:13:07 -08:00
|
|
|
|
2014-03-06 12:23:53 -08:00
|
|
|
tempFile = file.getAbsolutePath();
|
|
|
|
sendResult((tempFile == null) ? "" : tempFile);
|
|
|
|
|
|
|
|
if (tabId > -1 && !TextUtils.isEmpty(tempFile)) {
|
|
|
|
Tabs.registerOnTabsChangedListener(this);
|
|
|
|
}
|
2014-02-13 08:17:00 -08:00
|
|
|
} catch(IOException ex) {
|
|
|
|
Log.i(LOGTAG, "Error writing file", ex);
|
|
|
|
}
|
2012-07-13 11:06:24 -07:00
|
|
|
} else {
|
2014-02-13 08:17:00 -08:00
|
|
|
sendResult("");
|
2012-07-13 11:06:24 -07:00
|
|
|
}
|
|
|
|
}
|
2014-02-13 08:17:00 -08:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onLoaderReset(Loader<Cursor> loader) { }
|
2014-03-06 12:23:53 -08:00
|
|
|
|
|
|
|
/*Tabs.OnTabsChangedListener*/
|
|
|
|
// This cleans up our temp file. If it doesn't run, we just hope that Android
|
|
|
|
// will eventually does the cleanup for us.
|
|
|
|
@Override
|
|
|
|
public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
|
|
|
|
if (tab.getId() != tabId) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg == Tabs.TabEvents.LOCATION_CHANGE ||
|
|
|
|
msg == Tabs.TabEvents.CLOSED) {
|
|
|
|
ThreadUtils.postToBackgroundThread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
File f = new File(tempFile);
|
|
|
|
f.delete();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-03-24 14:40:54 -07:00
|
|
|
// Tabs' listener array is safe to modify during use: its
|
|
|
|
// iteration pattern is based on snapshots.
|
|
|
|
Tabs.unregisterOnTabsChangedListener(this);
|
2014-03-06 12:23:53 -08:00
|
|
|
}
|
|
|
|
}
|
2012-07-13 11:06:24 -07:00
|
|
|
}
|
2014-02-13 08:17:00 -08:00
|
|
|
|
2012-07-13 11:06:24 -07:00
|
|
|
}
|
2014-02-13 08:17:00 -08:00
|
|
|
|