Bug 780759 - Show a splashscreen when webapps launch. r=mfinkle

This commit is contained in:
Wes Johnston 2012-09-19 10:24:26 -07:00
Родитель 63dca591ca
Коммит 44b9cee4ef
12 изменённых файлов: 200 добавлений и 19 удалений

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

@ -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");