Bug 942611 - Delete temp files when tabs are closed. r=rewman

This commit is contained in:
Wes Johnston 2014-03-06 12:23:53 -08:00
parent ac6930af6b
commit 0a4cbfd9e5
3 changed files with 80 additions and 25 deletions

View File

@ -33,7 +33,7 @@ import java.util.List;
public class FilePicker implements GeckoEventListener {
private static final String LOGTAG = "GeckoFilePicker";
private static FilePicker sFilePicker;
private final Context mContext;
private final Context context;
public interface ResultHandler {
public void gotFile(String filename);
@ -46,7 +46,7 @@ public class FilePicker implements GeckoEventListener {
}
protected FilePicker(Context context) {
mContext = context;
this.context = context;
GeckoAppShell.getEventDispatcher().registerEventListener("FilePicker:Show", this);
}
@ -54,7 +54,8 @@ public class FilePicker implements GeckoEventListener {
public void handleMessage(String event, final JSONObject message) {
if (event.equals("FilePicker:Show")) {
String mimeType = "*/*";
String mode = message.optString("mode");
final String mode = message.optString("mode");
final int tabId = message.optInt("tabId", -1);
if ("mimeType".equals(mode))
mimeType = message.optString("mimeType");
@ -68,15 +69,17 @@ public class FilePicker implements GeckoEventListener {
} catch (JSONException ex) {
Log.i(LOGTAG, "Can't add filename to message " + filename);
}
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(
"FilePicker:Result", message.toString()));
}
});
}, tabId);
}
}
private void addActivities(Intent intent, HashMap<String, Intent> intents, HashMap<String, Intent> filters) {
PackageManager pm = mContext.getPackageManager();
PackageManager pm = context.getPackageManager();
List<ResolveInfo> lri = pm.queryIntentActivities(intent, 0);
for (ResolveInfo ri : lri) {
ComponentName cn = new ComponentName(ri.activityInfo.applicationInfo.packageName, ri.activityInfo.name);
@ -155,13 +158,13 @@ public class FilePicker implements GeckoEventListener {
private String getFilePickerTitle(String mimeType) {
if (mimeType.equals("audio/*")) {
return mContext.getString(R.string.filepicker_audio_title);
return context.getString(R.string.filepicker_audio_title);
} else if (mimeType.equals("image/*")) {
return mContext.getString(R.string.filepicker_image_title);
return context.getString(R.string.filepicker_image_title);
} else if (mimeType.equals("video/*")) {
return mContext.getString(R.string.filepicker_video_title);
return context.getString(R.string.filepicker_video_title);
} else {
return mContext.getString(R.string.filepicker_title);
return context.getString(R.string.filepicker_title);
}
}
@ -201,8 +204,8 @@ public class FilePicker implements GeckoEventListener {
* sends the file returned to the passed in handler. If a null handler is passed in, will still
* pick and launch the file picker, but will throw away the result.
*/
protected void showFilePickerAsync(String mimeType, final ResultHandler handler) {
final FilePickerResultHandler fileHandler = new FilePickerResultHandler(handler, mContext);
protected void showFilePickerAsync(String mimeType, final ResultHandler handler, final int tabId) {
final FilePickerResultHandler fileHandler = new FilePickerResultHandler(handler, context, tabId);
getFilePickerIntentAsync(mimeType, fileHandler, new IntentHandler() {
@Override
public void gotIntent(Intent intent) {

View File

@ -6,6 +6,7 @@ package org.mozilla.gecko;
import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.util.ActivityResultHandler;
import org.mozilla.gecko.util.ThreadUtils;
import android.app.Activity;
import android.content.ContentResolver;
@ -22,8 +23,10 @@ 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;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Log;
import android.os.Process;
import java.io.File;
import java.io.FileOutputStream;
@ -36,6 +39,7 @@ class FilePickerResultHandler implements ActivityResultHandler {
private static final String UPLOADS_DIR = "uploads";
protected final FilePicker.ResultHandler handler;
private final int tabId;
private final File cacheDir;
// this code is really hacky and doesn't belong anywhere so I'm putting it here for now
@ -43,7 +47,8 @@ class FilePickerResultHandler implements ActivityResultHandler {
private String mImageName = "";
/* Use this constructor to asynchronously listen for results */
public FilePickerResultHandler(FilePicker.ResultHandler handler, Context context) {
public FilePickerResultHandler(final FilePicker.ResultHandler handler, final Context context, final int tabId) {
this.tabId = tabId;
cacheDir = new File(context.getCacheDir(), UPLOADS_DIR);
this.handler = handler;
}
@ -119,16 +124,16 @@ class FilePickerResultHandler implements ActivityResultHandler {
}
private class VideoLoaderCallbacks implements LoaderCallbacks<Cursor> {
final private Uri mUri;
final private Uri uri;
public VideoLoaderCallbacks(Uri uri) {
mUri = uri;
this.uri = uri;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
return new CursorLoader(fa,
mUri,
uri,
new String[] { MediaStore.Video.Media.DATA },
null, // selection
null, // selectionArgs
@ -147,17 +152,20 @@ class FilePickerResultHandler implements ActivityResultHandler {
public void onLoaderReset(Loader<Cursor> loader) { }
}
private class FileLoaderCallbacks implements LoaderCallbacks<Cursor> {
final private Uri mUri;
private class FileLoaderCallbacks implements LoaderCallbacks<Cursor>,
Tabs.OnTabsChangedListener {
final private Uri uri;
private String tempFile;
public FileLoaderCallbacks(Uri uri) {
mUri = uri;
this.uri = uri;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
return new CursorLoader(fa,
mUri,
uri,
new String[] { OpenableColumns.DISPLAY_NAME },
null, // selection
null, // selectionArgs
@ -169,7 +177,7 @@ class FilePickerResultHandler implements ActivityResultHandler {
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
String fileName = "tmp_";
String fileName = "tmp_" + Process.myPid() + "-";
String fileExt = null;
int period;
@ -178,7 +186,7 @@ class FilePickerResultHandler implements ActivityResultHandler {
// Generate an extension if we don't already have one
if (name == null || (period = name.lastIndexOf('.')) == -1) {
String mimeType = cr.getType(mUri);
String mimeType = cr.getType(uri);
fileExt = "." + GeckoAppShell.getExtensionFromMimeType(mimeType);
} else {
fileExt = name.substring(period);
@ -191,7 +199,7 @@ class FilePickerResultHandler implements ActivityResultHandler {
File file = File.createTempFile(fileName, fileExt, cacheDir);
FileOutputStream fos = new FileOutputStream(file);
InputStream is = cr.openInputStream(mUri);
InputStream is = cr.openInputStream(uri);
byte[] buf = new byte[4096];
int len = is.read(buf);
while (len != -1) {
@ -200,8 +208,12 @@ class FilePickerResultHandler implements ActivityResultHandler {
}
fos.close();
String path = file.getAbsolutePath();
sendResult((path == null) ? "" : path);
tempFile = file.getAbsolutePath();
sendResult((tempFile == null) ? "" : tempFile);
if (tabId > -1 && !TextUtils.isEmpty(tempFile)) {
Tabs.registerOnTabsChangedListener(this);
}
} catch(IOException ex) {
Log.i(LOGTAG, "Error writing file", ex);
}
@ -212,6 +224,36 @@ class FilePickerResultHandler implements ActivityResultHandler {
@Override
public void onLoaderReset(Loader<Cursor> loader) { }
/*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();
}
});
// We're already on the UIThread, but we have to post this back to the uithread to avoid
// modifying the listener array while its being iterated through.
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
Tabs.unregisterOnTabsChangedListener(FileLoaderCallbacks.this);
}
});
}
}
}
}

View File

@ -197,8 +197,18 @@ FilePicker.prototype = {
_sendMessage: function() {
let msg = {
type: "FilePicker:Show",
guid: this.guid
guid: this.guid,
};
// Knowing the window lets us destroy any temp files when the tab is closed
// Other consumers of the file picker may have to either wait for Android
// to clean up the temp dir (not guaranteed) or clean up after themselves.
let win = Services.wm.getMostRecentWindow('navigator:browser');
let tab = win.BrowserApp.getTabForWindow(this._domWin.top)
if (tab) {
msg.tabId = tab.id;
}
if (!this._extensionsFilter && !this._mimeTypeFilter) {
// If neither filters is set show anything we can.
msg.mode = "mimeType";