mobile/android/base/FilePickerResultHandler.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko;
michael@0 6
michael@0 7 import org.mozilla.gecko.mozglue.GeckoLoader;
michael@0 8 import org.mozilla.gecko.util.ActivityResultHandler;
michael@0 9 import org.mozilla.gecko.util.ThreadUtils;
michael@0 10
michael@0 11 import android.app.Activity;
michael@0 12 import android.content.ContentResolver;
michael@0 13 import android.content.Context;
michael@0 14 import android.content.Intent;
michael@0 15 import android.database.Cursor;
michael@0 16 import android.net.Uri;
michael@0 17 import android.os.Bundle;
michael@0 18 import android.os.Environment;
michael@0 19 import android.provider.MediaStore;
michael@0 20 import android.provider.OpenableColumns;
michael@0 21 import android.support.v4.app.FragmentActivity;
michael@0 22 import android.support.v4.app.LoaderManager;
michael@0 23 import android.support.v4.app.LoaderManager.LoaderCallbacks;
michael@0 24 import android.support.v4.content.CursorLoader;
michael@0 25 import android.support.v4.content.Loader;
michael@0 26 import android.text.TextUtils;
michael@0 27 import android.text.format.Time;
michael@0 28 import android.util.Log;
michael@0 29 import android.os.Process;
michael@0 30
michael@0 31 import java.io.File;
michael@0 32 import java.io.FileOutputStream;
michael@0 33 import java.io.InputStream;
michael@0 34 import java.io.IOException;
michael@0 35
michael@0 36 class FilePickerResultHandler implements ActivityResultHandler {
michael@0 37 private static final String LOGTAG = "GeckoFilePickerResultHandler";
michael@0 38 private static final String UPLOADS_DIR = "uploads";
michael@0 39
michael@0 40 protected final FilePicker.ResultHandler handler;
michael@0 41 private final int tabId;
michael@0 42 private final File cacheDir;
michael@0 43
michael@0 44 // this code is really hacky and doesn't belong anywhere so I'm putting it here for now
michael@0 45 // until I can come up with a better solution.
michael@0 46 private String mImageName = "";
michael@0 47
michael@0 48 /* Use this constructor to asynchronously listen for results */
michael@0 49 public FilePickerResultHandler(final FilePicker.ResultHandler handler, final Context context, final int tabId) {
michael@0 50 this.tabId = tabId;
michael@0 51 cacheDir = new File(context.getCacheDir(), UPLOADS_DIR);
michael@0 52 this.handler = handler;
michael@0 53 }
michael@0 54
michael@0 55 private void sendResult(String res) {
michael@0 56 if (handler != null) {
michael@0 57 handler.gotFile(res);
michael@0 58 }
michael@0 59 }
michael@0 60
michael@0 61 @Override
michael@0 62 public void onActivityResult(int resultCode, Intent intent) {
michael@0 63 if (resultCode != Activity.RESULT_OK) {
michael@0 64 sendResult("");
michael@0 65 return;
michael@0 66 }
michael@0 67
michael@0 68 // Camera results won't return an Intent. Use the file name we passed to the original intent.
michael@0 69 if (intent == null) {
michael@0 70 if (mImageName != null) {
michael@0 71 File file = new File(Environment.getExternalStorageDirectory(), mImageName);
michael@0 72 sendResult(file.getAbsolutePath());
michael@0 73 } else {
michael@0 74 sendResult("");
michael@0 75 }
michael@0 76 return;
michael@0 77 }
michael@0 78
michael@0 79 Uri uri = intent.getData();
michael@0 80 if (uri == null) {
michael@0 81 sendResult("");
michael@0 82 return;
michael@0 83 }
michael@0 84
michael@0 85 // Some file pickers may return a file uri
michael@0 86 if ("file".equals(uri.getScheme())) {
michael@0 87 String path = uri.getPath();
michael@0 88 sendResult(path == null ? "" : path);
michael@0 89 return;
michael@0 90 }
michael@0 91
michael@0 92 final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
michael@0 93 final LoaderManager lm = fa.getSupportLoaderManager();
michael@0 94 // Finally, Video pickers and some file pickers may return a content provider.
michael@0 95 Cursor cursor = null;
michael@0 96 try {
michael@0 97 // Try a query to make sure the expected columns exist
michael@0 98 final ContentResolver cr = fa.getContentResolver();
michael@0 99 cursor = cr.query(uri, new String[] { MediaStore.Video.Media.DATA }, null, null, null);
michael@0 100
michael@0 101 int index = cursor.getColumnIndex(MediaStore.Video.Media.DATA);
michael@0 102 if (index >= 0) {
michael@0 103 lm.initLoader(intent.hashCode(), null, new VideoLoaderCallbacks(uri));
michael@0 104 return;
michael@0 105 }
michael@0 106 } catch(Exception ex) {
michael@0 107 // We'll try a different loader below
michael@0 108 } finally {
michael@0 109 if (cursor != null) {
michael@0 110 cursor.close();
michael@0 111 }
michael@0 112 }
michael@0 113
michael@0 114 lm.initLoader(uri.hashCode(), null, new FileLoaderCallbacks(uri));
michael@0 115 return;
michael@0 116 }
michael@0 117
michael@0 118 public String generateImageName() {
michael@0 119 Time now = new Time();
michael@0 120 now.setToNow();
michael@0 121 mImageName = now.format("%Y-%m-%d %H.%M.%S") + ".jpg";
michael@0 122 return mImageName;
michael@0 123 }
michael@0 124
michael@0 125 private class VideoLoaderCallbacks implements LoaderCallbacks<Cursor> {
michael@0 126 final private Uri uri;
michael@0 127 public VideoLoaderCallbacks(Uri uri) {
michael@0 128 this.uri = uri;
michael@0 129 }
michael@0 130
michael@0 131 @Override
michael@0 132 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
michael@0 133 final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
michael@0 134 return new CursorLoader(fa,
michael@0 135 uri,
michael@0 136 new String[] { MediaStore.Video.Media.DATA },
michael@0 137 null, // selection
michael@0 138 null, // selectionArgs
michael@0 139 null); // sortOrder
michael@0 140 }
michael@0 141
michael@0 142 @Override
michael@0 143 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
michael@0 144 if (cursor.moveToFirst()) {
michael@0 145 String res = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA));
michael@0 146
michael@0 147 // Some pickers (the KitKat Documents one for instance) won't return a temporary file here.
michael@0 148 // Fall back to the normal FileLoader if we didn't find anything.
michael@0 149 if (TextUtils.isEmpty(res)) {
michael@0 150 tryFileLoaderCallback();
michael@0 151 return;
michael@0 152 }
michael@0 153
michael@0 154 sendResult(res);
michael@0 155 } else {
michael@0 156 tryFileLoaderCallback();
michael@0 157 }
michael@0 158 }
michael@0 159
michael@0 160 private void tryFileLoaderCallback() {
michael@0 161 final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
michael@0 162 final LoaderManager lm = fa.getSupportLoaderManager();
michael@0 163 lm.initLoader(uri.hashCode(), null, new FileLoaderCallbacks(uri));
michael@0 164 }
michael@0 165
michael@0 166 @Override
michael@0 167 public void onLoaderReset(Loader<Cursor> loader) { }
michael@0 168 }
michael@0 169
michael@0 170 private class FileLoaderCallbacks implements LoaderCallbacks<Cursor>,
michael@0 171 Tabs.OnTabsChangedListener {
michael@0 172 final private Uri uri;
michael@0 173 private String tempFile;
michael@0 174
michael@0 175 public FileLoaderCallbacks(Uri uri) {
michael@0 176 this.uri = uri;
michael@0 177 }
michael@0 178
michael@0 179 @Override
michael@0 180 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
michael@0 181 final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
michael@0 182 return new CursorLoader(fa,
michael@0 183 uri,
michael@0 184 new String[] { OpenableColumns.DISPLAY_NAME },
michael@0 185 null, // selection
michael@0 186 null, // selectionArgs
michael@0 187 null); // sortOrder
michael@0 188 }
michael@0 189
michael@0 190 @Override
michael@0 191 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
michael@0 192 if (cursor.moveToFirst()) {
michael@0 193 String name = cursor.getString(0);
michael@0 194 // tmp filenames must be at least 3 characters long. Add a prefix to make sure that happens
michael@0 195 String fileName = "tmp_" + Process.myPid() + "-";
michael@0 196 String fileExt = null;
michael@0 197 int period;
michael@0 198
michael@0 199 final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
michael@0 200 final ContentResolver cr = fa.getContentResolver();
michael@0 201
michael@0 202 // Generate an extension if we don't already have one
michael@0 203 if (name == null || (period = name.lastIndexOf('.')) == -1) {
michael@0 204 String mimeType = cr.getType(uri);
michael@0 205 fileExt = "." + GeckoAppShell.getExtensionFromMimeType(mimeType);
michael@0 206 } else {
michael@0 207 fileExt = name.substring(period);
michael@0 208 fileName += name.substring(0, period);
michael@0 209 }
michael@0 210
michael@0 211 // Now write the data to the temp file
michael@0 212 try {
michael@0 213 cacheDir.mkdir();
michael@0 214
michael@0 215 File file = File.createTempFile(fileName, fileExt, cacheDir);
michael@0 216 FileOutputStream fos = new FileOutputStream(file);
michael@0 217 InputStream is = cr.openInputStream(uri);
michael@0 218 byte[] buf = new byte[4096];
michael@0 219 int len = is.read(buf);
michael@0 220 while (len != -1) {
michael@0 221 fos.write(buf, 0, len);
michael@0 222 len = is.read(buf);
michael@0 223 }
michael@0 224 fos.close();
michael@0 225
michael@0 226 tempFile = file.getAbsolutePath();
michael@0 227 sendResult((tempFile == null) ? "" : tempFile);
michael@0 228
michael@0 229 if (tabId > -1 && !TextUtils.isEmpty(tempFile)) {
michael@0 230 Tabs.registerOnTabsChangedListener(this);
michael@0 231 }
michael@0 232 } catch(IOException ex) {
michael@0 233 Log.i(LOGTAG, "Error writing file", ex);
michael@0 234 }
michael@0 235 } else {
michael@0 236 sendResult("");
michael@0 237 }
michael@0 238 }
michael@0 239
michael@0 240 @Override
michael@0 241 public void onLoaderReset(Loader<Cursor> loader) { }
michael@0 242
michael@0 243 /*Tabs.OnTabsChangedListener*/
michael@0 244 // This cleans up our temp file. If it doesn't run, we just hope that Android
michael@0 245 // will eventually does the cleanup for us.
michael@0 246 @Override
michael@0 247 public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
michael@0 248 if (tab.getId() != tabId) {
michael@0 249 return;
michael@0 250 }
michael@0 251
michael@0 252 if (msg == Tabs.TabEvents.LOCATION_CHANGE ||
michael@0 253 msg == Tabs.TabEvents.CLOSED) {
michael@0 254 ThreadUtils.postToBackgroundThread(new Runnable() {
michael@0 255 @Override
michael@0 256 public void run() {
michael@0 257 File f = new File(tempFile);
michael@0 258 f.delete();
michael@0 259 }
michael@0 260 });
michael@0 261
michael@0 262 // Tabs' listener array is safe to modify during use: its
michael@0 263 // iteration pattern is based on snapshots.
michael@0 264 Tabs.unregisterOnTabsChangedListener(this);
michael@0 265 }
michael@0 266 }
michael@0 267 }
michael@0 268
michael@0 269 }
michael@0 270

mercurial