зеркало из https://github.com/mozilla/gecko-dev.git
Bug 780759 - Show a splashscreen when webapps launch. r=mfinkle
This commit is contained in:
Родитель
63dca591ca
Коммит
44b9cee4ef
|
@ -959,7 +959,7 @@ abstract public class GeckoApp
|
|||
} else if (event.equals("WebApps:Open")) {
|
||||
String url = message.getString("uri");
|
||||
String origin = message.getString("origin");
|
||||
Intent intent = GeckoAppShell.getWebAppIntent(url, origin, false);
|
||||
Intent intent = GeckoAppShell.getWebAppIntent(url, origin, "", null);
|
||||
if (intent == null)
|
||||
return;
|
||||
startActivity(intent);
|
||||
|
|
|
@ -68,6 +68,7 @@ import android.view.View;
|
|||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Toast;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
|
@ -795,17 +796,17 @@ public class GeckoAppShell
|
|||
}
|
||||
|
||||
public static File installWebApp(String aTitle, String aURI, String aUniqueURI, String aIconURL) {
|
||||
int index = WebAppAllocator.getInstance(GeckoApp.mAppContext).findAndAllocateIndex(aUniqueURI);
|
||||
int index = WebAppAllocator.getInstance(GeckoApp.mAppContext).findAndAllocateIndex(aUniqueURI, aTitle, aIconURL);
|
||||
GeckoProfile profile = GeckoProfile.get(GeckoApp.mAppContext, "webapp" + index);
|
||||
createShortcut(aTitle, aURI, aUniqueURI, aIconURL, "webapp");
|
||||
return profile.getDir();
|
||||
}
|
||||
|
||||
public static Intent getWebAppIntent(String aURI, String aUniqueURI, boolean forInstall) {
|
||||
public static Intent getWebAppIntent(String aURI, String aUniqueURI, String aTitle, Bitmap aIcon) {
|
||||
int index;
|
||||
|
||||
if (forInstall)
|
||||
index = WebAppAllocator.getInstance(GeckoApp.mAppContext).findAndAllocateIndex(aUniqueURI);
|
||||
if (aIcon != null && !TextUtils.isEmpty(aTitle))
|
||||
index = WebAppAllocator.getInstance(GeckoApp.mAppContext).findAndAllocateIndex(aUniqueURI, aTitle, aIcon);
|
||||
else
|
||||
index = WebAppAllocator.getInstance(GeckoApp.mAppContext).getIndexForApp(aUniqueURI);
|
||||
|
||||
|
@ -853,7 +854,7 @@ public class GeckoAppShell
|
|||
// the intent to be launched by the shortcut
|
||||
Intent shortcutIntent;
|
||||
if (aType.equalsIgnoreCase(SHORTCUT_TYPE_WEBAPP)) {
|
||||
shortcutIntent = getWebAppIntent(aURI, aUniqueURI, true);
|
||||
shortcutIntent = getWebAppIntent(aURI, aUniqueURI, aTitle, aIcon);
|
||||
} else {
|
||||
shortcutIntent = new Intent();
|
||||
shortcutIntent.setAction(GeckoApp.ACTION_BOOKMARK);
|
||||
|
@ -889,8 +890,8 @@ public class GeckoAppShell
|
|||
// the intent to be launched by the shortcut
|
||||
Intent shortcutIntent;
|
||||
if (aType.equalsIgnoreCase(SHORTCUT_TYPE_WEBAPP)) {
|
||||
int index = WebAppAllocator.getInstance(GeckoApp.mAppContext).findAndAllocateIndex(aUniqueURI);
|
||||
shortcutIntent = getWebAppIntent(aURI, aUniqueURI, false);
|
||||
int index = WebAppAllocator.getInstance(GeckoApp.mAppContext).getIndexForApp(aUniqueURI);
|
||||
shortcutIntent = getWebAppIntent(aURI, aUniqueURI, "", null);
|
||||
if (shortcutIntent == null)
|
||||
return;
|
||||
} else {
|
||||
|
|
|
@ -85,7 +85,7 @@ public class LauncherShortcuts extends Activity {
|
|||
} catch (JSONException e) { }
|
||||
|
||||
WebAppAllocator allocator = WebAppAllocator.getInstance(this);
|
||||
int index = allocator.findAndAllocateIndex(uri);
|
||||
int index = allocator.findAndAllocateIndex(uri, title, favicon);
|
||||
Intent shortcutintent = new Intent(GeckoApp.ACTION_WEBAPP_PREFIX + index);
|
||||
shortcutintent.setClassName(this, GeckoApp.mAppContext.getPackageName() + ".WebApps$WebApp" + index);
|
||||
shortcutintent.putExtra("args", "--webapp=" + uri);
|
||||
|
|
|
@ -429,6 +429,7 @@ RES_XML = \
|
|||
|
||||
RES_ANIM = \
|
||||
res/anim/grow_fade_in.xml \
|
||||
res/anim/grow_fade_in_center.xml \
|
||||
res/anim/shrink_fade_out.xml \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -15,8 +15,19 @@ import android.widget.LinearLayout;
|
|||
import android.widget.TextView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ImageView;
|
||||
import android.view.Display;
|
||||
import android.graphics.Point;
|
||||
|
||||
import java.net.URL;
|
||||
import java.io.File;
|
||||
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
|
@ -25,12 +36,15 @@ import org.mozilla.gecko.WebAppAllocator;
|
|||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.DoorHangerPopup;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class WebApp extends GeckoApp {
|
||||
private URL mOrigin;
|
||||
private TextView mTitlebarText = null;
|
||||
private View mTitlebar = null;
|
||||
private static final String LOGTAG = "WebApp";
|
||||
private View mSplashscreen = null;
|
||||
|
||||
protected int getIndex() { return 0; }
|
||||
|
||||
|
@ -44,6 +58,8 @@ public class WebApp extends GeckoApp {
|
|||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
overridePendingTransition(R.anim.grow_fade_in_center, android.R.anim.fade_out);
|
||||
showSplash();
|
||||
|
||||
String action = getIntent().getAction();
|
||||
Bundle extras = getIntent().getExtras();
|
||||
|
@ -72,6 +88,48 @@ public class WebApp extends GeckoApp {
|
|||
}
|
||||
}
|
||||
|
||||
private void showSplash() {
|
||||
mSplashscreen = (RelativeLayout) findViewById(R.id.splashscreen);
|
||||
|
||||
SharedPreferences prefs = getSharedPreferences("webapps", Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
|
||||
|
||||
// get the favicon dominant color, stored when the app was installed
|
||||
int[] colors = new int[2];
|
||||
int dominantColor = prefs.getInt(WebAppAllocator.iconKey(getIndex()), -1);
|
||||
|
||||
// now lighten it, to ensure that the icon stands out in the center
|
||||
float[] f = new float[3];
|
||||
Color.colorToHSV(dominantColor, f);
|
||||
f[2] = Math.min(f[2]*2, 1.0f);
|
||||
colors[0] = Color.HSVToColor(255, f);
|
||||
|
||||
// now generate a second, slightly darker version of the same color
|
||||
f[2] *= 0.75;
|
||||
colors[1] = Color.HSVToColor(255, f);
|
||||
|
||||
// Draw the background gradient
|
||||
GradientDrawable gd = new GradientDrawable(GradientDrawable.Orientation.TL_BR, colors);
|
||||
gd.setGradientType(GradientDrawable.RADIAL_GRADIENT);
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
gd.setGradientCenter(0.5f, 0.5f);
|
||||
gd.setGradientRadius(Math.max(display.getWidth()/2, display.getHeight()/2));
|
||||
mSplashscreen.setBackgroundDrawable((Drawable)gd);
|
||||
|
||||
// look for a logo.png in the profile dir and show it. If we can't find a logo show nothing
|
||||
File profile = getProfile().getDir();
|
||||
File logoFile = new File(profile, "logo.png");
|
||||
if (logoFile.exists()) {
|
||||
ImageView image = (ImageView)findViewById(R.id.splashscreen_icon);
|
||||
Drawable d = Drawable.createFromPath(logoFile.getPath());
|
||||
image.setImageDrawable(d);
|
||||
|
||||
Animation fadein = AnimationUtils.loadAnimation(this, R.anim.grow_fade_in_center);
|
||||
fadein.setStartOffset(500);
|
||||
fadein.setDuration(1000);
|
||||
image.startAnimation(fadein);
|
||||
}
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return "@ANDROID_PACKAGE_NAME@";
|
||||
}
|
||||
|
@ -143,6 +201,29 @@ public class WebApp extends GeckoApp {
|
|||
Log.e(LOGTAG, "Unable to parse url: ", ex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LOADED:
|
||||
if (mSplashscreen.getVisibility() == View.VISIBLE) {
|
||||
Animation fadeout = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
|
||||
fadeout.setAnimationListener(new Animation.AnimationListener() {
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
mSplashscreen.setVisibility(View.GONE);
|
||||
}
|
||||
public void onAnimationRepeat(Animation animation) { }
|
||||
public void onAnimationStart(Animation animation) { }
|
||||
});
|
||||
mSplashscreen.startAnimation(fadeout);
|
||||
}
|
||||
break;
|
||||
case START:
|
||||
if (mSplashscreen.getVisibility() == View.VISIBLE) {
|
||||
View area = findViewById(R.id.splashscreen_progress);
|
||||
area.setVisibility(View.VISIBLE);
|
||||
Animation fadein = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
|
||||
fadein.setDuration(1000);
|
||||
area.startAnimation(fadein);
|
||||
}
|
||||
break;
|
||||
}
|
||||
super.onTabChanged(tab, msg, data);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,17 @@ package org.mozilla.gecko;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
|
||||
public class WebAppAllocator {
|
||||
private final String LOGTAG = "GeckoWebAppAllocator";
|
||||
// The number of WebApp# and WEBAPP# activites/apps/intents
|
||||
private final static int MAX_WEB_APPS = 100;
|
||||
|
||||
|
@ -44,20 +53,38 @@ public class WebAppAllocator {
|
|||
mPrefs = context.getSharedPreferences("webapps", Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
|
||||
}
|
||||
|
||||
static String appKey(int index) {
|
||||
public static String appKey(int index) {
|
||||
return "app" + index;
|
||||
}
|
||||
|
||||
public synchronized int findAndAllocateIndex(String app) {
|
||||
static public String iconKey(int index) {
|
||||
return "icon" + index;
|
||||
}
|
||||
|
||||
public synchronized int findAndAllocateIndex(String app, String name, String aIconData) {
|
||||
byte[] raw = Base64.decode(aIconData.substring(22), Base64.DEFAULT);
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(raw, 0, raw.length);
|
||||
return findAndAllocateIndex(app, name, bitmap);
|
||||
}
|
||||
|
||||
public synchronized int findAndAllocateIndex(String app, String name, Bitmap aIcon) {
|
||||
int index = getIndexForApp(app);
|
||||
if (index != -1)
|
||||
return index;
|
||||
|
||||
int color = 0;
|
||||
try {
|
||||
color = BitmapUtils.getDominantColor(aIcon);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_WEB_APPS; ++i) {
|
||||
if (!mPrefs.contains(appKey(i))) {
|
||||
// found unused index i
|
||||
mPrefs.edit()
|
||||
.putString(appKey(i), app)
|
||||
.putInt(iconKey(i), color)
|
||||
.apply();
|
||||
return i;
|
||||
}
|
||||
|
@ -78,7 +105,7 @@ public class WebAppAllocator {
|
|||
}
|
||||
|
||||
public synchronized String getAppForIndex(int index) {
|
||||
return mPrefs.getString(appKey(index), null);
|
||||
return mPrefs.getString(appKey(index), null);
|
||||
}
|
||||
|
||||
public synchronized int releaseIndexForApp(String app) {
|
||||
|
@ -93,6 +120,7 @@ public class WebAppAllocator {
|
|||
public synchronized void releaseIndex(int index) {
|
||||
mPrefs.edit()
|
||||
.remove(appKey(index))
|
||||
.remove(iconKey(index))
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
android:launchMode="singleInstance"
|
||||
android:taskAffinity="org.mozilla.gecko.WEBAPP@APPNUM@"
|
||||
android:process=":@ANDROID_PACKAGE_NAME@.WebApp@APPNUM@"
|
||||
android:theme="@style/Gecko.NoActionBar">
|
||||
android:theme="@style/Gecko.Translucent.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="org.mozilla.gecko.WEBAPP@APPNUM@" />
|
||||
</intent-filter>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="250">
|
||||
|
||||
<scale android:fromXScale="0.5"
|
||||
android:toXScale="1.0"
|
||||
android:fromYScale="0.5"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"/>
|
||||
|
||||
<alpha android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"/>
|
||||
|
||||
</set>
|
|
@ -2,7 +2,8 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/main_layout"
|
||||
android:orientation="vertical"
|
||||
style="@style/Screen">
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<LinearLayout android:id="@+id/webapp_titlebar"
|
||||
android:visibility="gone"
|
||||
|
@ -26,6 +27,29 @@
|
|||
|
||||
<include layout="@layout/shared_ui_components"/>
|
||||
|
||||
<RelativeLayout android:id="@+id/splashscreen"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" >
|
||||
|
||||
<ImageView android:id="@+id/splashscreen_icon"
|
||||
android:minWidth="128dip"
|
||||
android:minHeight="128dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"/>
|
||||
|
||||
<ProgressBar android:id="@+id/splashscreen_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:paddingBottom="30dip"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -43,6 +43,11 @@
|
|||
<item name="android:windowNoTitle">false</item>
|
||||
</style>
|
||||
|
||||
<style name="Gecko.Translucent.NoActionBar">
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Gecko.App">
|
||||
<item name="android:windowBackground">@drawable/abouthome_bg_repeat</item>
|
||||
<item name="android:panelBackground">@drawable/menu_panel_bg</item>
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Gecko.Translucent.NoActionBar" parent="Gecko.Translucent"/>
|
||||
|
||||
<style name="Gecko.App">
|
||||
<item name="android:windowBackground">@drawable/abouthome_bg_repeat</item>
|
||||
</style>
|
||||
|
|
|
@ -6325,13 +6325,13 @@ var WebappsUI = {
|
|||
// Add a homescreen shortcut -- we can't use createShortcut, since we need to pass
|
||||
// a unique ID for Android webapp allocation
|
||||
this.makeBase64Icon(this.getBiggestIcon(manifest.icons, Services.io.newURI(aData.app.origin, null, null)),
|
||||
(function(icon) {
|
||||
(function(scaledIcon, fullsizeIcon) {
|
||||
let profilePath = sendMessageToJava({
|
||||
gecko: {
|
||||
type: "WebApps:Install",
|
||||
name: manifest.name,
|
||||
launchPath: manifest.fullLaunchPath(),
|
||||
iconURL: icon,
|
||||
iconURL: scaledIcon,
|
||||
uniqueURI: aData.app.origin
|
||||
}
|
||||
});
|
||||
|
@ -6351,6 +6351,20 @@ var WebappsUI = {
|
|||
let defaultPrefsFile = file.clone();
|
||||
defaultPrefsFile.append(this.DEFAULT_PREFS_FILENAME);
|
||||
this.writeDefaultPrefs(defaultPrefsFile, prefs);
|
||||
|
||||
// also save the icon so that it can be used in the splash screen
|
||||
try {
|
||||
let iconFile = file.clone();
|
||||
iconFile.append("logo.png");
|
||||
let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
|
||||
persist.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
|
||||
persist.persistFlags |= Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
|
||||
|
||||
let source = Services.io.newURI(fullsizeIcon, "UTF8", null);
|
||||
persist.saveURI(source, null, null, null, null, iconFile);
|
||||
} catch(ex) {
|
||||
console.log(ex);
|
||||
}
|
||||
}
|
||||
DOMApplicationRegistry.confirmInstall(aData, false, file);
|
||||
}).bind(this));
|
||||
|
@ -6363,7 +6377,7 @@ var WebappsUI = {
|
|||
if (aPrefs.length > 0) {
|
||||
let data = JSON.stringify(aPrefs);
|
||||
|
||||
var ostream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
|
||||
let ostream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
|
||||
ostream.init(aFile, -1, -1, 0);
|
||||
|
||||
let istream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
|
||||
|
@ -6413,9 +6427,15 @@ var WebappsUI = {
|
|||
let favicon = new Image();
|
||||
favicon.onload = function() {
|
||||
ctx.drawImage(favicon, 0, 0, size, size);
|
||||
let base64icon = canvas.toDataURL("image/png", "");
|
||||
let scaledIcon = canvas.toDataURL("image/png", "");
|
||||
|
||||
canvas.width = favicon.width;
|
||||
canvas.height = favicon.height;
|
||||
ctx.drawImage(favicon, 0, 0, favicon.width, favicon.height);
|
||||
let fullsizeIcon = canvas.toDataURL("image/png", "");
|
||||
|
||||
canvas = null;
|
||||
aCallbackFunction.call(null, base64icon);
|
||||
aCallbackFunction.call(null, scaledIcon, fullsizeIcon);
|
||||
};
|
||||
favicon.onerror = function() {
|
||||
Cu.reportError("CreateShortcut: favicon image load error");
|
||||
|
|
Загрузка…
Ссылка в новой задаче