Bug 1258450 - Move references to SnackbarHelper out of GeckoAppShell. r=snorp,sebastian
☠☠ backed out by e2787bb704a7 ☠ ☠
authorNick Alexander <nalexander@mozilla.com>
Sun, 20 Mar 2016 14:41:39 -0700
changeset 290843 0476a0d0575b4b07c404eb15bc5a943ae04d0289
parent 290842 476cdaa0ce08b0808c3892d3d5ee55f701504666
child 290844 7a434b18855a9439a98480a2261509ab5596f315
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, sebastian
bugs1258450
milestone48.0a1
Bug 1258450 - Move references to SnackbarHelper out of GeckoAppShell. r=snorp,sebastian The existing code assumes an Activity, not just a Context, but doesn't statically guarantee it. This patch is safe because it dynamically type-checks, but it would be better to declare the member to be an Activity. MozReview-Commit-ID: 9AigV055I5j
mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
mobile/android/base/java/org/mozilla/gecko/widget/GeckoActionProvider.java
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
@@ -2,28 +2,25 @@
  * 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;
 
 import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.OutputStream;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
 import java.net.MalformedURLException;
 import java.net.Proxy;
 import java.net.URISyntaxException;
-import java.net.URL;
 import java.net.URLConnection;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.StringTokenizer;
@@ -33,24 +30,22 @@ import java.util.concurrent.ConcurrentHa
 import android.annotation.SuppressLint;
 import org.mozilla.gecko.annotation.JNITarget;
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PanZoomController;
-import org.mozilla.gecko.mozglue.ContextUtils;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
 import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoRequest;
 import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
 import org.mozilla.gecko.util.HardwareUtils;
-import org.mozilla.gecko.util.IOUtils;
 import org.mozilla.gecko.util.NativeEventListener;
 import org.mozilla.gecko.util.NativeJSContainer;
 import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.util.ProxySelector;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.widget.ExternalIntentDuringPrivateBrowsingPromptFragment;
 
 import android.Manifest;
@@ -91,34 +86,31 @@ import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.os.Vibrator;
 import android.provider.Browser;
 import android.provider.Settings;
-import android.support.design.widget.Snackbar;
 import android.support.v4.app.FragmentActivity;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.util.Base64;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.Surface;
 import android.view.SurfaceView;
 import android.view.TextureView;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.MimeTypeMap;
-import android.webkit.URLUtil;
 import android.widget.AbsoluteLayout;
 
 public class GeckoAppShell
 {
     private static final String LOGTAG = "GeckoAppShell";
 
     // We have static members only.
     private GeckoAppShell() { }
@@ -2532,94 +2524,16 @@ public class GeckoAppShell
                 return "PROXY " + proxy.address().toString();
             case SOCKS:
                 return "SOCKS " + proxy.address().toString();
         }
 
         return "DIRECT";
     }
 
-    /* Downloads the URI pointed to by a share intent, and alters the intent to point to the locally stored file.
-     */
-    public static void downloadImageForIntent(final Intent intent) {
-        final String src = ContextUtils.getStringExtra(intent, Intent.EXTRA_TEXT);
-        if (src == null) {
-            showImageShareFailureSnackbar();
-            return;
-        }
-
-        final File dir = GeckoApp.getTempDirectory();
-
-        if (dir == null) {
-            showImageShareFailureSnackbar();
-            return;
-        }
-
-        GeckoApp.deleteTempFiles();
-
-        String type = intent.getType();
-        OutputStream os = null;
-        try {
-            // Create a temporary file for the image
-            if (src.startsWith("data:")) {
-                final int dataStart = src.indexOf(",");
-
-                String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(type);
-
-                // If we weren't given an explicit mimetype, try to dig one out of the data uri.
-                if (TextUtils.isEmpty(extension) && dataStart > 5) {
-                    type = src.substring(5, dataStart).replace(";base64", "");
-                    extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(type);
-                }
-
-                final File imageFile = File.createTempFile("image", "." + extension, dir);
-                os = new FileOutputStream(imageFile);
-
-                byte[] buf = Base64.decode(src.substring(dataStart + 1), Base64.DEFAULT);
-                os.write(buf);
-
-                // Only alter the intent when we're sure everything has worked
-                intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(imageFile));
-            } else {
-                InputStream is = null;
-                try {
-                    final byte[] buf = new byte[2048];
-                    final URL url = new URL(src);
-                    final String filename = URLUtil.guessFileName(src, null, type);
-                    is = url.openStream();
-
-                    final File imageFile = new File(dir, filename);
-                    os = new FileOutputStream(imageFile);
-
-                    int length;
-                    while ((length = is.read(buf)) != -1) {
-                        os.write(buf, 0, length);
-                    }
-
-                    // Only alter the intent when we're sure everything has worked
-                    intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(imageFile));
-                } finally {
-                    IOUtils.safeStreamClose(is);
-                }
-            }
-        } catch(IOException ex) {
-            // If something went wrong, we'll just leave the intent un-changed
-        } finally {
-            IOUtils.safeStreamClose(os);
-        }
-    }
-
-    // Don't fail silently, tell the user that we weren't able to share the image
-    private static final void showImageShareFailureSnackbar() {
-        SnackbarHelper.showSnackbar((Activity) getContext(),
-                getApplicationContext().getString(R.string.share_image_failed),
-                Snackbar.LENGTH_LONG
-        );
-    }
-
     @WrapForJNI(allowMultithread = true)
     static InputStream createInputStream(URLConnection connection) throws IOException {
         return connection.getInputStream();
     }
 
     private static class BitmapConnection extends URLConnection {
         private Bitmap bitmap;
 
--- a/mobile/android/base/java/org/mozilla/gecko/widget/GeckoActionProvider.java
+++ b/mobile/android/base/java/org/mozilla/gecko/widget/GeckoActionProvider.java
@@ -1,36 +1,52 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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.widget;
 
+import android.app.Activity;
+import android.net.Uri;
+import android.support.design.widget.Snackbar;
+import android.util.Base64;
 import android.view.Menu;
-import org.mozilla.gecko.GeckoAppShell;
+
+import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.SnackbarHelper;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.menu.QuickShareBarActionView;
+import org.mozilla.gecko.mozglue.ContextUtils;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
 import org.mozilla.gecko.menu.MenuItemSwitcherLayout;
+import org.mozilla.gecko.util.IOUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.view.MenuItem;
 import android.view.MenuItem.OnMenuItemClickListener;
 import android.view.SubMenu;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.text.TextUtils;
+import android.webkit.MimeTypeMap;
+import android.webkit.URLUtil;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
 
 public class GeckoActionProvider {
     private static final int MAX_HISTORY_SIZE_DEFAULT = 2;
 
     /**
      * A listener to know when a target was selected.
@@ -227,17 +243,17 @@ public class GeckoActionProvider {
             if (launchIntent != null) {
                 // This may cause a download to happen. Make sure we're on the background thread.
                 ThreadUtils.postToBackgroundThread(new Runnable() {
                     @Override
                     public void run() {
                         // Share image downloads the image before sharing it.
                         String type = launchIntent.getType();
                         if (Intent.ACTION_SEND.equals(launchIntent.getAction()) && type != null && type.startsWith("image/")) {
-                            GeckoAppShell.downloadImageForIntent(launchIntent);
+                            downloadImageForIntent(launchIntent);
                         }
 
                         launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
                         mContext.startActivity(launchIntent);
                     }
                 });
             }
 
@@ -265,9 +281,85 @@ public class GeckoActionProvider {
         }
     }
 
     public enum ActionViewType {
         DEFAULT,
         QUICK_SHARE_ICON,
         CONTEXT_MENU,
     }
+
+
+    /**
+     * Downloads the URI pointed to by a share intent, and alters the intent to point to the
+     * locally stored file.
+     *
+     * @param intent share intent to alter in place.
+     */
+    public void downloadImageForIntent(final Intent intent) {
+        final String src = ContextUtils.getStringExtra(intent, Intent.EXTRA_TEXT);
+        final File dir = GeckoApp.getTempDirectory();
+
+        if (src == null || dir == null) {
+            // We should be, but currently aren't, statically guaranteed an Activity context.
+            // Try our best.
+            if (mContext instanceof Activity) {
+                SnackbarHelper.showSnackbar((Activity) mContext,
+                        mContext.getApplicationContext().getString(R.string.share_image_failed),
+                        Snackbar.LENGTH_LONG);
+            }
+            return;
+        }
+
+        GeckoApp.deleteTempFiles();
+
+        String type = intent.getType();
+        OutputStream os = null;
+        try {
+            // Create a temporary file for the image
+            if (src.startsWith("data:")) {
+                final int dataStart = src.indexOf(",");
+
+                String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(type);
+
+                // If we weren't given an explicit mimetype, try to dig one out of the data uri.
+                if (TextUtils.isEmpty(extension) && dataStart > 5) {
+                    type = src.substring(5, dataStart).replace(";base64", "");
+                    extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(type);
+                }
+
+                final File imageFile = File.createTempFile("image", "." + extension, dir);
+                os = new FileOutputStream(imageFile);
+
+                byte[] buf = Base64.decode(src.substring(dataStart + 1), Base64.DEFAULT);
+                os.write(buf);
+
+                // Only alter the intent when we're sure everything has worked
+                intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(imageFile));
+            } else {
+                InputStream is = null;
+                try {
+                    final byte[] buf = new byte[2048];
+                    final URL url = new URL(src);
+                    final String filename = URLUtil.guessFileName(src, null, type);
+                    is = url.openStream();
+
+                    final File imageFile = new File(dir, filename);
+                    os = new FileOutputStream(imageFile);
+
+                    int length;
+                    while ((length = is.read(buf)) != -1) {
+                        os.write(buf, 0, length);
+                    }
+
+                    // Only alter the intent when we're sure everything has worked
+                    intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(imageFile));
+                } finally {
+                    IOUtils.safeStreamClose(is);
+                }
+            }
+        } catch(IOException ex) {
+            // If something went wrong, we'll just leave the intent un-changed
+        } finally {
+            IOUtils.safeStreamClose(os);
+        }
+    }
 }