Bug 1258450 - Add GeckoInterface.createShortcut. r=snorp

This moves some Fennec-specific home-screen icon manipulations out of
GeckoAppShell.  A GeckoView interface can follow.

MozReview-Commit-ID: 7OhRAT9Agdh

--HG--
extra : rebase_source : c5bc8a871a22c5892b320068dea6501ed8884d8e
extra : source : f39f05fa8df3717c57452ac44a16e4a86d9cbd2c
This commit is contained in:
Nick Alexander 2016-03-30 10:21:29 -07:00
Родитель d17f3149d7
Коммит b591ff3d43
4 изменённых файлов: 139 добавлений и 121 удалений

Просмотреть файл

@ -140,4 +140,9 @@ public class BaseGeckoInterface implements GeckoAppShell.GeckoInterface {
// Bug 908792: Implement this
@Override
public void invalidateOptionsMenu() {}
@Override
public void createShortcut(String title, String URI) {
// By default, do nothing.
}
}

Просмотреть файл

@ -5,12 +5,15 @@
package org.mozilla.gecko;
import android.content.ContentResolver;
import android.widget.AdapterView;
import android.widget.Button;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.URLMetadataTable;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.gfx.FullScreenState;
import org.mozilla.gecko.gfx.Layer;
@ -111,6 +114,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -1818,6 +1822,66 @@ public abstract class GeckoApp
AppConstants.USER_AGENT_FENNEC_MOBILE;
}
@Override
public void createShortcut(final String title, final String URI) {
ThreadUtils.assertOnBackgroundThread();
final BrowserDB db = GeckoProfile.get(getApplicationContext()).getDB();
final ContentResolver cr = getContext().getContentResolver();
final Map<String, Map<String, Object>> metadata = db.getURLMetadata().getForURLs(cr,
Collections.singletonList(URI),
Collections.singletonList(URLMetadataTable.TOUCH_ICON_COLUMN)
);
final Map<String, Object> row = metadata.get(URI);
String touchIconURL = null;
if (row != null) {
touchIconURL = (String) row.get(URLMetadataTable.TOUCH_ICON_COLUMN);
}
OnFaviconLoadedListener listener = new OnFaviconLoadedListener() {
@Override
public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) {
doCreateShortcut(title, url, favicon);
}
};
// Retrieve the icon while bypassing the cache. Homescreen icon creation is a one-off event, hence it isn't
// useful to cache these icons. (Android takes care of storing homescreen icons after a shortcut
// has been created.)
// The cache is also (currently) limited to 32dp, hence we explicitly need to avoid accessing those icons.
// If touchIconURL is null, then Favicons falls back to finding the best possible favicon for
// the site URI, hence we can use this call even when there is no touchIcon defined.
Favicons.getPreferredSizeFaviconForPage(getApplicationContext(), URI, touchIconURL, listener);
}
private void doCreateShortcut(final String aTitle, final String aURI, final Bitmap aIcon) {
// The intent to be launched by the shortcut.
Intent shortcutIntent = new Intent();
shortcutIntent.setAction(GeckoApp.ACTION_HOMESCREEN_SHORTCUT);
shortcutIntent.setData(Uri.parse(aURI));
shortcutIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, BitmapUtils.getLauncherIcon(getApplicationContext(), aIcon, GeckoAppShell.getPreferredIconSize()));
if (aTitle != null) {
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aTitle);
} else {
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aURI);
}
// Do not allow duplicate items.
intent.putExtra("duplicate", false);
intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
getApplicationContext().sendBroadcast(intent);
}
private void processAlertCallback(SafeIntent intent) {
String alertName = "";
String alertCookie = "";

Просмотреть файл

@ -22,7 +22,6 @@ import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
@ -32,15 +31,11 @@ import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
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.db.BrowserDB;
import org.mozilla.gecko.db.URLMetadataTable;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.gfx.PanZoomController;
@ -78,10 +73,7 @@ import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
@ -213,9 +205,6 @@ public class GeckoAppShell
static private int sDensityDpi;
static private int sScreenDepth;
/* Default colors. */
private static final float[] DEFAULT_LAUNCHER_ICON_HSV = { 32.0f, 1.0f, 1.0f };
/* Is the value in sVibrationEndTime valid? */
private static boolean sVibrationMaybePlaying;
@ -833,62 +822,11 @@ public class GeckoAppShell
// This is the entry point from nsIShellService.
@WrapForJNI
public static void createShortcut(final String aTitle, final String aURI) {
ThreadUtils.assertOnBackgroundThread();
final BrowserDB db = GeckoProfile.get(getApplicationContext()).getDB();
final ContentResolver cr = getContext().getContentResolver();
final Map<String, Map<String, Object>> metadata = db.getURLMetadata().getForURLs(cr,
Collections.singletonList(aURI),
Collections.singletonList(URLMetadataTable.TOUCH_ICON_COLUMN)
);
final Map<String, Object> row = metadata.get(aURI);
String touchIconURL = null;
if (row != null) {
touchIconURL = (String) row.get(URLMetadataTable.TOUCH_ICON_COLUMN);
final GeckoInterface geckoInterface = getGeckoInterface();
if (geckoInterface == null) {
return;
}
OnFaviconLoadedListener listener = new OnFaviconLoadedListener() {
@Override
public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) {
doCreateShortcut(aTitle, url, favicon);
}
};
// Retrieve the icon while bypassing the cache. Homescreen icon creation is a one-off event, hence it isn't
// useful to cache these icons. (Android takes care of storing homescreen icons after a shortcut
// has been created.)
// The cache is also (currently) limited to 32dp, hence we explicitly need to avoid accessing those icons.
// If touchIconURL is null, then Favicons falls back to finding the best possible favicon for
// the site URI, hence we can use this call even when there is no touchIcon defined.
Favicons.getPreferredSizeFaviconForPage(getApplicationContext(), aURI, touchIconURL, listener);
}
private static void doCreateShortcut(final String aTitle, final String aURI, final Bitmap aIcon) {
// The intent to be launched by the shortcut.
Intent shortcutIntent = new Intent();
shortcutIntent.setAction(GeckoApp.ACTION_HOMESCREEN_SHORTCUT);
shortcutIntent.setData(Uri.parse(aURI));
shortcutIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, getLauncherIcon(aIcon));
if (aTitle != null) {
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aTitle);
} else {
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aURI);
}
// Do not allow duplicate items.
intent.putExtra("duplicate", false);
intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
getApplicationContext().sendBroadcast(intent);
geckoInterface.createShortcut(aTitle, aURI);
}
@JNITarget
@ -910,60 +848,6 @@ public class GeckoAppShell
}
}
static private Bitmap getLauncherIcon(Bitmap aSource) {
final int kOffset = 6;
final int kRadius = 5;
int size = getPreferredIconSize();
int insetSize = aSource != null ? size * 2 / 3 : size;
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
// draw a base color
Paint paint = new Paint();
if (aSource == null) {
// If we aren't drawing a favicon, just use an orange color.
paint.setColor(Color.HSVToColor(DEFAULT_LAUNCHER_ICON_HSV));
canvas.drawRoundRect(new RectF(kOffset, kOffset, size - kOffset, size - kOffset), kRadius, kRadius, paint);
} else if (aSource.getWidth() >= insetSize || aSource.getHeight() >= insetSize) {
// Otherwise, if the icon is large enough, just draw it.
Rect iconBounds = new Rect(0, 0, size, size);
canvas.drawBitmap(aSource, null, iconBounds, null);
return bitmap;
} else {
// otherwise use the dominant color from the icon + a layer of transparent white to lighten it somewhat
int color = BitmapUtils.getDominantColor(aSource);
paint.setColor(color);
canvas.drawRoundRect(new RectF(kOffset, kOffset, size - kOffset, size - kOffset), kRadius, kRadius, paint);
paint.setColor(Color.argb(100, 255, 255, 255));
canvas.drawRoundRect(new RectF(kOffset, kOffset, size - kOffset, size - kOffset), kRadius, kRadius, paint);
}
// draw the overlay
Bitmap overlay = BitmapUtils.decodeResource(getApplicationContext(), R.drawable.home_bg);
canvas.drawBitmap(overlay, null, new Rect(0, 0, size, size), null);
// draw the favicon
if (aSource == null)
aSource = BitmapUtils.decodeResource(getApplicationContext(), R.drawable.home_star);
// by default, we scale the icon to this size
int sWidth = insetSize / 2;
int sHeight = sWidth;
int halfSize = size / 2;
canvas.drawBitmap(aSource,
null,
new Rect(halfSize - sWidth,
halfSize - sHeight,
halfSize + sWidth,
halfSize + sHeight),
null);
return bitmap;
}
@WrapForJNI(stubName = "GetHandlersForMimeTypeWrapper")
static String[] getHandlersForMimeType(String aMimeType, String aAction) {
Intent intent = getIntentForActionString(aAction);
@ -2209,6 +2093,13 @@ public class GeckoAppShell
public AbsoluteLayout getPluginContainer();
public void notifyCheckUpdateResult(String result);
public void invalidateOptionsMenu();
/**
* Create a shortcut -- generally a home-screen icon -- linking the given title to the given URI.
* @param title of URI to link to.
* @param URI to link to.
*/
public void createShortcut(String title, String URI);
};
private static GeckoInterface sGeckoInterface;

Просмотреть файл

@ -26,6 +26,9 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@ -34,6 +37,9 @@ import android.util.Base64;
import android.util.Log;
public final class BitmapUtils {
/* Default colors. */
private static final float[] DEFAULT_LAUNCHER_ICON_HSV = { 32.0f, 1.0f, 1.0f };
private static final String LOGTAG = "GeckoBitmapUtils";
private BitmapUtils() {}
@ -429,5 +435,57 @@ public final class BitmapUtils {
}
return icon;
}
}
public static Bitmap getLauncherIcon(Context context, Bitmap aSource, int size) {
final int kOffset = 6;
final int kRadius = 5;
int insetSize = aSource != null ? size * 2 / 3 : size;
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
// draw a base color
Paint paint = new Paint();
if (aSource == null) {
// If we aren't drawing a favicon, just use an orange color.
paint.setColor(Color.HSVToColor(DEFAULT_LAUNCHER_ICON_HSV));
canvas.drawRoundRect(new RectF(kOffset, kOffset, size - kOffset, size - kOffset), kRadius, kRadius, paint);
} else if (aSource.getWidth() >= insetSize || aSource.getHeight() >= insetSize) {
// Otherwise, if the icon is large enough, just draw it.
Rect iconBounds = new Rect(0, 0, size, size);
canvas.drawBitmap(aSource, null, iconBounds, null);
return bitmap;
} else {
// otherwise use the dominant color from the icon + a layer of transparent white to lighten it somewhat
int color = BitmapUtils.getDominantColor(aSource);
paint.setColor(color);
canvas.drawRoundRect(new RectF(kOffset, kOffset, size - kOffset, size - kOffset), kRadius, kRadius, paint);
paint.setColor(Color.argb(100, 255, 255, 255));
canvas.drawRoundRect(new RectF(kOffset, kOffset, size - kOffset, size - kOffset), kRadius, kRadius, paint);
}
// draw the overlay
Bitmap overlay = BitmapUtils.decodeResource(context, R.drawable.home_bg);
canvas.drawBitmap(overlay, null, new Rect(0, 0, size, size), null);
// draw the favicon
if (aSource == null)
aSource = BitmapUtils.decodeResource(context, R.drawable.home_star);
// by default, we scale the icon to this size
int sWidth = insetSize / 2;
int sHeight = sWidth;
int halfSize = size / 2;
canvas.drawBitmap(aSource,
null,
new Rect(halfSize - sWidth,
halfSize - sHeight,
halfSize + sWidth,
halfSize + sHeight),
null);
return bitmap;
}
}