mobile/android/base/webapp/WebappImpl.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.

     1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 package org.mozilla.gecko.webapp;
     8 import java.io.File;
     9 import java.io.IOException;
    10 import java.net.URI;
    12 import org.json.JSONException;
    13 import org.json.JSONObject;
    14 import org.mozilla.gecko.GeckoApp;
    15 import org.mozilla.gecko.GeckoAppShell;
    16 import org.mozilla.gecko.GeckoEvent;
    17 import org.mozilla.gecko.GeckoThread;
    18 import org.mozilla.gecko.R;
    19 import org.mozilla.gecko.Tab;
    20 import org.mozilla.gecko.Tabs;
    21 import org.mozilla.gecko.webapp.InstallHelper.InstallCallback;
    23 import android.content.Intent;
    24 import android.content.pm.PackageManager.NameNotFoundException;
    25 import android.graphics.Color;
    26 import android.graphics.drawable.Drawable;
    27 import android.graphics.drawable.GradientDrawable;
    28 import android.net.Uri;
    29 import android.os.Bundle;
    30 import android.util.Log;
    31 import android.view.Display;
    32 import android.view.View;
    33 import android.view.animation.Animation;
    34 import android.view.animation.AnimationUtils;
    35 import android.widget.ImageView;
    36 import android.widget.TextView;
    38 public class WebappImpl extends GeckoApp implements InstallCallback {
    39     private static final String LOGTAG = "GeckoWebappImpl";
    41     private URI mOrigin;
    42     private TextView mTitlebarText = null;
    43     private View mTitlebar = null;
    45     private View mSplashscreen;
    47     private boolean mIsApk = true;
    48     private ApkResources mApkResources;
    49     private String mManifestUrl;
    50     private String mAppName;
    52     protected int getIndex() { return 0; }
    54     @Override
    55     public int getLayout() { return R.layout.web_app; }
    57     @Override
    58     public boolean hasTabsSideBar() { return false; }
    60     @Override
    61     public void onCreate(Bundle savedInstance)
    62     {
    64         String action = getIntent().getAction();
    65         Bundle extras = getIntent().getExtras();
    66         if (extras == null) {
    67             extras = savedInstance;
    68         }
    70         if (extras == null) {
    71             extras = new Bundle();
    72         }
    74         boolean isInstalled = extras.getBoolean("isInstalled", false);
    75         String packageName = extras.getString("packageName");
    77         if (packageName == null) {
    78             Log.w(LOGTAG, "no package name; treating as legacy shortcut");
    80             mIsApk = false;
    82             // Shortcut apps are already installed.
    83             isInstalled = true;
    85             Uri data = getIntent().getData();
    86             if (data == null) {
    87                 Log.wtf(LOGTAG, "can't get manifest URL from shortcut data");
    88                 setResult(RESULT_CANCELED);
    89                 finish();
    90                 return;
    91             }
    92             mManifestUrl = data.toString();
    94             String shortcutName = extras.getString(Intent.EXTRA_SHORTCUT_NAME);
    95             mAppName = shortcutName != null ? shortcutName : "Web App";
    96         } else {
    97             try {
    98                 mApkResources = new ApkResources(this, packageName);
    99             } catch (NameNotFoundException e) {
   100                 Log.e(LOGTAG, "Can't find package for webapp " + packageName, e);
   101                 setResult(RESULT_CANCELED);
   102                 finish();
   103                 return;
   104             }
   106             mManifestUrl = mApkResources.getManifestUrl();
   107             mAppName = mApkResources.getAppName();
   108         }
   110         // start Gecko.
   111         super.onCreate(savedInstance);
   113         mTitlebarText = (TextView)findViewById(R.id.webapp_title);
   114         mTitlebar = findViewById(R.id.webapp_titlebar);
   115         mSplashscreen = findViewById(R.id.splashscreen);
   117         Allocator allocator = Allocator.getInstance(this);
   118         int index = getIndex();
   120         // We have to migrate old prefs before getting the origin because origin
   121         // is one of the prefs we might migrate.
   122         allocator.maybeMigrateOldPrefs(index);
   124         String origin = allocator.getOrigin(index);
   125         boolean isInstallCompleting = (origin == null);
   127         if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning) || !isInstalled || isInstallCompleting) {
   128             // Show the splash screen if we need to start Gecko, or we need to install this.
   129             overridePendingTransition(R.anim.grow_fade_in_center, android.R.anim.fade_out);
   130             showSplash();
   131         } else {
   132             mSplashscreen.setVisibility(View.GONE);
   133         }
   135         if (!isInstalled || isInstallCompleting) {
   136             InstallHelper installHelper = new InstallHelper(getApplicationContext(), mApkResources, this);
   137             if (!isInstalled) {
   138                 // start the vanilla install.
   139                 try {
   140                     installHelper.startInstall(getDefaultProfileName());
   141                 } catch (IOException e) {
   142                     Log.e(LOGTAG, "Couldn't install packaged app", e);
   143                 }
   144             } else {
   145                 // an install is already happening, so we should let it complete.
   146                 Log.i(LOGTAG, "Waiting for existing install to complete");
   147                 installHelper.registerGeckoListener();
   148             }
   149             return;
   150         } else {
   151             launchWebapp(origin);
   152         }
   154         setTitle(mAppName);
   155     }
   157     @Override
   158     protected String getURIFromIntent(Intent intent) {
   159         String uri = super.getURIFromIntent(intent);
   160         if (uri != null) {
   161             return uri;
   162         }
   163         // This is where we construct the URL from the Intent from the
   164         // the synthesized APK.
   166         // TODO Translate AndroidIntents into WebActivities here.
   167         if (mIsApk) {
   168             return mApkResources.getManifestUrl();
   169         }
   171         // If this is a legacy shortcut, then we should have been able to get
   172         // the URI from the intent data.  Otherwise, we should have been able
   173         // to get it from the APK resources.  So we should never get here.
   174         Log.wtf(LOGTAG, "Couldn't get URI from intent nor APK resources");
   175         return null;
   176     }
   178     @Override
   179     protected void loadStartupTab(String uri) {
   180         // Load a tab so it's available for any code that assumes a tab
   181         // before the app tab itself is loaded in BrowserApp._loadWebapp.
   182         super.loadStartupTab("about:blank");
   183     }
   185     private void showSplash() {
   187         // get the favicon dominant color, stored when the app was installed
   188         int dominantColor = Allocator.getInstance().getColor(getIndex());
   190         setBackgroundGradient(dominantColor);
   192         ImageView image = (ImageView)findViewById(R.id.splashscreen_icon);
   193         Drawable d = null;
   195         if (mIsApk) {
   196             Uri uri = mApkResources.getAppIconUri();
   197             image.setImageURI(uri);
   198             d = image.getDrawable();
   199         } else {
   200             // look for a logo.png in the profile dir and show it. If we can't find a logo show nothing
   201             File profile = getProfile().getDir();
   202             File logoFile = new File(profile, "logo.png");
   203             if (logoFile.exists()) {
   204                 d = Drawable.createFromPath(logoFile.getPath());
   205                 image.setImageDrawable(d);
   206             }
   207         }
   209         if (d != null) {
   210             Animation fadein = AnimationUtils.loadAnimation(this, R.anim.grow_fade_in_center);
   211             fadein.setStartOffset(500);
   212             fadein.setDuration(1000);
   213             image.startAnimation(fadein);
   214         }
   215     }
   217     public void setBackgroundGradient(int dominantColor) {
   218         int[] colors = new int[2];
   219         // now lighten it, to ensure that the icon stands out in the center
   220         float[] f = new float[3];
   221         Color.colorToHSV(dominantColor, f);
   222         f[2] = Math.min(f[2]*2, 1.0f);
   223         colors[0] = Color.HSVToColor(255, f);
   225         // now generate a second, slightly darker version of the same color
   226         f[2] *= 0.75;
   227         colors[1] = Color.HSVToColor(255, f);
   229         // Draw the background gradient
   230         GradientDrawable gd = new GradientDrawable(GradientDrawable.Orientation.TL_BR, colors);
   231         gd.setGradientType(GradientDrawable.RADIAL_GRADIENT);
   232         Display display = getWindowManager().getDefaultDisplay();
   233         gd.setGradientCenter(0.5f, 0.5f);
   234         gd.setGradientRadius(Math.max(display.getWidth()/2, display.getHeight()/2));
   235         mSplashscreen.setBackgroundDrawable(gd);
   236     }
   238     /* (non-Javadoc)
   239      * @see org.mozilla.gecko.GeckoApp#getDefaultProfileName()
   240      */
   241     @Override
   242     protected String getDefaultProfileName() {
   243         return "webapp" + getIndex();
   244     }
   246     @Override
   247     protected boolean getSessionRestoreState(Bundle savedInstanceState) {
   248         // for now webapps never restore your session
   249         return false;
   250     }
   252     @Override
   253     public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
   254         switch(msg) {
   255             case SELECTED:
   256             case LOCATION_CHANGE:
   257                 if (Tabs.getInstance().isSelectedTab(tab)) {
   258                     final String urlString = tab.getURL();
   260                     // Don't show the titlebar for about:blank, which we load
   261                     // into the initial tab we create while waiting for the app
   262                     // to load.
   263                     if (urlString != null && urlString.equals("about:blank")) {
   264                         mTitlebar.setVisibility(View.GONE);
   265                         return;
   266                     }
   268                     final URI uri;
   270                     try {
   271                         uri = new URI(urlString);
   272                     } catch (java.net.URISyntaxException ex) {
   273                         mTitlebarText.setText(urlString);
   275                         // If we can't parse the url, and its an app protocol hide
   276                         // the titlebar and return, otherwise show the titlebar
   277                         // and the full url
   278                         if (urlString != null && !urlString.startsWith("app://")) {
   279                             mTitlebar.setVisibility(View.VISIBLE);
   280                         } else {
   281                             mTitlebar.setVisibility(View.GONE);
   282                         }
   283                         return;
   284                     }
   286                     if (mOrigin != null && mOrigin.getHost().equals(uri.getHost())) {
   287                         mTitlebar.setVisibility(View.GONE);
   288                     } else {
   289                         mTitlebarText.setText(uri.getScheme() + "://" + uri.getHost());
   290                         mTitlebar.setVisibility(View.VISIBLE);
   291                     }
   292                 }
   293                 break;
   294             case LOADED:
   295                 hideSplash();
   296                 break;
   297             case START:
   298                 if (mSplashscreen != null && mSplashscreen.getVisibility() == View.VISIBLE) {
   299                     View area = findViewById(R.id.splashscreen_progress);
   300                     area.setVisibility(View.VISIBLE);
   301                     Animation fadein = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
   302                     fadein.setDuration(1000);
   303                     area.startAnimation(fadein);
   304                 }
   305                 break;
   306         }
   307         super.onTabChanged(tab, msg, data);
   308     }
   310     protected void hideSplash() {
   311         if (mSplashscreen != null && mSplashscreen.getVisibility() == View.VISIBLE) {
   312             Animation fadeout = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
   313             fadeout.setAnimationListener(new Animation.AnimationListener() {
   314                 @Override
   315                 public void onAnimationEnd(Animation animation) {
   316                   mSplashscreen.setVisibility(View.GONE);
   317                 }
   318                 @Override
   319                 public void onAnimationRepeat(Animation animation) { }
   320                 @Override
   321                 public void onAnimationStart(Animation animation) { }
   322             });
   323             mSplashscreen.startAnimation(fadeout);
   324         }
   325     }
   327     @Override
   328     public void installCompleted(InstallHelper installHelper, String event, JSONObject message) {
   329         if (event == null) {
   330             return;
   331         }
   333         if (event.equals("Webapps:Postinstall")) {
   334             String origin = message.optString("origin");
   335             launchWebapp(origin);
   336         }
   337     }
   339     @Override
   340     public void installErrored(InstallHelper installHelper, Exception exception) {
   341         Log.e(LOGTAG, "Install errored", exception);
   342     }
   344     private void setOrigin(String origin) {
   345         try {
   346             mOrigin = new URI(origin);
   347         } catch (java.net.URISyntaxException ex) {
   348             // If this isn't an app: URL, just settle for not having an origin.
   349             if (!origin.startsWith("app://")) {
   350                 return;
   351             }
   353             // If that failed fall back to the origin stored in the shortcut.
   354             if (!mIsApk) {
   355                 Log.i(LOGTAG, "Origin is app: URL; falling back to intent URL");
   356                 Uri data = getIntent().getData();
   357                 if (data != null) {
   358                     try {
   359                         mOrigin = new URI(data.toString());
   360                     } catch (java.net.URISyntaxException ex2) {
   361                         Log.e(LOGTAG, "Unable to parse intent URL: ", ex);
   362                     }
   363                 }
   364             }
   365         }
   366     }
   368     public void launchWebapp(String origin) {
   369         setOrigin(origin);
   371         try {
   372             JSONObject launchObject = new JSONObject();
   373             launchObject.putOpt("url", mManifestUrl);
   374             launchObject.putOpt("name", mAppName);
   375             Log.i(LOGTAG, "Trying to launch: " + launchObject);
   376             GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Webapps:Load", launchObject.toString()));
   377         } catch (JSONException e) {
   378             Log.e(LOGTAG, "Error populating launch message", e);
   379         }
   380     }
   382     @Override
   383     protected boolean getIsDebuggable() {
   384         if (mIsApk) {
   385             return mApkResources.isDebuggable();
   386         }
   388         // This is a legacy shortcut, which didn't provide a way to determine
   389         // that the app is debuggable, so we say the app is not debuggable.
   390         return false;
   391     }
   392 }

mercurial