зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1347605 - Ensure standalone pwa's are singletons
This commit is contained in:
Родитель
bfd998a06c
Коммит
7b024ea9c8
|
@ -317,6 +317,12 @@
|
|||
<activity android:name="org.mozilla.gecko.webapps.WebAppActivity"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar" />
|
||||
|
||||
<!-- Declare a predefined number of WebApp<num> activities. These are
|
||||
used so that each web app can launch in its own activity. -->
|
||||
#define FRAGMENT WebAppManifestFragment.xml.frag.in
|
||||
#include WebAppFragmentRepeater.inc
|
||||
|
||||
|
||||
<!-- Service to handle requests from overlays. -->
|
||||
<service android:name="org.mozilla.gecko.overlays.service.OverlayActionService" />
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#define APPNUM 0
|
||||
#include @FRAGMENT@
|
||||
|
||||
#define APPNUM 1
|
||||
#include @FRAGMENT@
|
||||
|
||||
#define APPNUM 2
|
||||
#include @FRAGMENT@
|
||||
|
||||
#define APPNUM 3
|
||||
#include @FRAGMENT@
|
||||
|
||||
#define APPNUM 4
|
||||
#include @FRAGMENT@
|
||||
|
||||
#define APPNUM 5
|
||||
#include @FRAGMENT@
|
||||
|
||||
#define APPNUM 6
|
||||
#include @FRAGMENT@
|
||||
|
||||
#define APPNUM 7
|
||||
#include @FRAGMENT@
|
||||
|
||||
#define APPNUM 8
|
||||
#include @FRAGMENT@
|
||||
|
||||
#define APPNUM 9
|
||||
#include @FRAGMENT@
|
||||
|
||||
#undef APPNUM
|
||||
#undef FRAGMENT
|
|
@ -0,0 +1,9 @@
|
|||
<activity android:name="org.mozilla.gecko.webapps.WebApps$WebApp@APPNUM@"
|
||||
android:label="WebApp"
|
||||
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|locale|layoutDirection|smallestScreenSize|screenLayout"
|
||||
android:windowSoftInputMode="stateUnspecified|adjustResize"
|
||||
android:theme="@style/Gecko.App"
|
||||
android:taskAffinity="org.mozilla.gecko.webapps.WebApps@APPNUM@"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true"
|
||||
/>
|
|
@ -2242,9 +2242,7 @@ public abstract class GeckoApp
|
|||
GeckoAppShell.setGeckoInterface(this);
|
||||
GeckoAppShell.setScreenOrientationDelegate(this);
|
||||
|
||||
if (lastSelectedTabId >= 0 && (lastActiveGeckoApp == null || lastActiveGeckoApp.get() != this)) {
|
||||
Tabs.getInstance().selectTab(lastSelectedTabId);
|
||||
}
|
||||
restoreLastSelectedTab();
|
||||
|
||||
int newOrientation = getResources().getConfiguration().orientation;
|
||||
if (GeckoScreenOrientation.getInstance().update(newOrientation)) {
|
||||
|
@ -2296,6 +2294,12 @@ public abstract class GeckoApp
|
|||
Restrictions.update(this);
|
||||
}
|
||||
|
||||
protected void restoreLastSelectedTab() {
|
||||
if (lastSelectedTabId >= 0 && (lastActiveGeckoApp == null || lastActiveGeckoApp.get() != this)) {
|
||||
Tabs.getInstance().selectTab(lastSelectedTabId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
package org.mozilla.gecko;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
|
||||
import org.mozilla.gecko.webapps.WebAppActivity;
|
||||
import org.mozilla.gecko.webapps.WebAppIndexer;
|
||||
import org.mozilla.gecko.customtabs.CustomTabsActivity;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.mozglue.SafeIntent;
|
||||
|
@ -89,11 +91,11 @@ public class LauncherActivity extends Activity {
|
|||
}
|
||||
|
||||
private void dispatchWebAppIntent() {
|
||||
Intent intent = new Intent(getIntent());
|
||||
intent.setClassName(getApplicationContext(), WebAppActivity.class.getName());
|
||||
|
||||
filterFlags(intent);
|
||||
|
||||
final Intent intent = new Intent(getIntent());
|
||||
final String manifestPath = getIntent().getStringExtra(WebAppActivity.MANIFEST_PATH);
|
||||
final int index = WebAppIndexer.getInstance().getIndexForManifest(manifestPath, this);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setClassName(this, WebAppIndexer.WEBAPP_CLASS + index);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,9 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.Window;
|
||||
|
@ -25,7 +27,9 @@ import org.mozilla.gecko.GeckoApp;
|
|||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.icons.decoders.FaviconDecoder;
|
||||
import org.mozilla.gecko.mozglue.SafeIntent;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.util.ColorUtil;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.FileUtils;
|
||||
|
@ -41,11 +45,7 @@ public class WebAppActivity extends GeckoApp {
|
|||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
String manifestPath = getIntent().getStringExtra(WebAppActivity.MANIFEST_PATH);
|
||||
if (manifestPath != null) {
|
||||
updateFromManifest(manifestPath);
|
||||
}
|
||||
loadManifest(getIntent());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,6 +53,35 @@ public class WebAppActivity extends GeckoApp {
|
|||
return R.layout.webapp_activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* In case this activity is reused (the user has opened > 10 current web apps)
|
||||
* we check that app launched is still within the same host as the
|
||||
* shortcut has set, if not we reload the homescreens url
|
||||
*/
|
||||
@Override
|
||||
protected void onNewIntent(Intent externalIntent) {
|
||||
|
||||
restoreLastSelectedTab();
|
||||
|
||||
final SafeIntent intent = new SafeIntent(externalIntent);
|
||||
final String launchUrl = intent.getDataString();
|
||||
final String currentUrl = Tabs.getInstance().getSelectedTab().getURL();
|
||||
final boolean isSameDomain = Uri.parse(currentUrl).getHost()
|
||||
.equals(Uri.parse(launchUrl).getHost());
|
||||
|
||||
if (!isSameDomain) {
|
||||
loadManifest(externalIntent);
|
||||
Tabs.getInstance().loadUrl(launchUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadManifest(Intent intent) {
|
||||
String manifestPath = intent.getStringExtra(WebAppActivity.MANIFEST_PATH);
|
||||
if (manifestPath != null) {
|
||||
updateFromManifest(manifestPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFromManifest(String manifestPath) {
|
||||
try {
|
||||
final File manifestFile = new File(manifestPath);
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.webapps;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
|
||||
/**
|
||||
* WebAppIndexer lets us create bookmarks that behave like android applications
|
||||
* we create 10 slots of WebAppN in the manifest, when a bookmark is launched
|
||||
* we take its manifest and assign it an index, subsequent launches of that
|
||||
* bookmark are given the same index and therefore reopen the previous activity.
|
||||
* We limit this to 10 spaces and recycle the least recently used index once
|
||||
* we hit the limit, this means if the user can actively use 10 webapps at the same
|
||||
* time, more than that and the last used will be replaced
|
||||
**/
|
||||
|
||||
public class WebAppIndexer {
|
||||
|
||||
public static final String WEBAPP_CLASS = "org.mozilla.gecko.webapps.WebApps$WebApp";
|
||||
|
||||
private final String mPrefNumSavedEntries = "WebAppIndexer.numActivities";
|
||||
private final String mPrefActivityIndex = "WebAppIndexer.index";
|
||||
private final String mPrefActivityId = "WebAppIndexer.manifest";
|
||||
|
||||
private static final int MAX_ACTIVITIES = 10;
|
||||
private static final int INVALID_INDEX = -1;
|
||||
|
||||
private ArrayList<ActivityEntry> mActivityList = new ArrayList<ActivityEntry>();
|
||||
|
||||
private static class ActivityEntry {
|
||||
public final int index;
|
||||
public final String manifest;
|
||||
ActivityEntry(int _index, String _manifest) {
|
||||
index = _index;
|
||||
manifest = _manifest;
|
||||
}
|
||||
}
|
||||
|
||||
private WebAppIndexer() { }
|
||||
private final static WebAppIndexer INSTANCE = new WebAppIndexer();
|
||||
public static WebAppIndexer getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public int getIndexForManifest(String manifest, Context context) {
|
||||
|
||||
if (mActivityList.size() == 0) {
|
||||
loadActivityList(context);
|
||||
}
|
||||
|
||||
int index = getManifestIndex(manifest);
|
||||
|
||||
// If we havent assigned this manifest an index then reassign the
|
||||
// least recently used index
|
||||
if (index == INVALID_INDEX) {
|
||||
index = mActivityList.get(0).index;
|
||||
final ActivityEntry newEntry = new ActivityEntry(index, manifest);
|
||||
mActivityList.set(0, newEntry);
|
||||
}
|
||||
|
||||
// Put the index at the back of the queue to be recycled
|
||||
markActivityUsed(index, manifest, context);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private int getManifestIndex(String manifest) {
|
||||
for (int i = mActivityList.size() - 1; i >= 0; i--) {
|
||||
if (manifest.equals(mActivityList.get(i).manifest)) {
|
||||
return mActivityList.get(i).index;
|
||||
}
|
||||
}
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
|
||||
private void markActivityUsed(int index, String manifest, Context context) {
|
||||
final int elementIndex = findActivityElement(index);
|
||||
final ActivityEntry updatedEntry = new ActivityEntry(index, manifest);
|
||||
mActivityList.remove(elementIndex);
|
||||
mActivityList.add(updatedEntry);
|
||||
storeActivityList(context);
|
||||
}
|
||||
|
||||
private int findActivityElement(int index) {
|
||||
for (int elementIndex = 0; elementIndex < mActivityList.size(); elementIndex++) {
|
||||
if (mActivityList.get(elementIndex).index == index) {
|
||||
return elementIndex;
|
||||
}
|
||||
}
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
|
||||
// Store the list of assigned indexes in sharedPrefs because the LauncherActivity
|
||||
// is likely to be killed and restarted between webapp launches
|
||||
@UiThread
|
||||
private void storeActivityList(Context context) {
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(context);
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.clear();
|
||||
editor.putInt(mPrefNumSavedEntries, mActivityList.size());
|
||||
for (int i = 0; i < mActivityList.size(); ++i) {
|
||||
editor.putInt(mPrefActivityIndex + i, mActivityList.get(i).index);
|
||||
editor.putString(mPrefActivityId + i, mActivityList.get(i).manifest);
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void loadActivityList(Context context) {
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(context);
|
||||
final int numSavedEntries = prefs.getInt(mPrefNumSavedEntries, 0);
|
||||
for (int i = 0; i < numSavedEntries; ++i) {
|
||||
int index = prefs.getInt(mPrefActivityIndex + i, i);
|
||||
String manifest = prefs.getString(mPrefActivityId + i, null);
|
||||
ActivityEntry entry = new ActivityEntry(index, manifest);
|
||||
mActivityList.add(entry);
|
||||
}
|
||||
|
||||
// Fill rest of the list with null spacers to be assigned
|
||||
final int leftToFill = MAX_ACTIVITIES - mActivityList.size();
|
||||
for (int i = 0; i < leftToFill; ++i) {
|
||||
ActivityEntry entry = new ActivityEntry(i, null);
|
||||
mActivityList.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.webapps;
|
||||
|
||||
/**
|
||||
* 10 predefined slots for homescreen webapps, in LauncherActivity
|
||||
* launched webapps will be given an index (via WebAppIndexer) that
|
||||
* points to one of these class names
|
||||
**/
|
||||
|
||||
public final class WebApps {
|
||||
public static class WebApp0 extends WebAppActivity { }
|
||||
public static class WebApp1 extends WebAppActivity { }
|
||||
public static class WebApp2 extends WebAppActivity { }
|
||||
public static class WebApp3 extends WebAppActivity { }
|
||||
public static class WebApp4 extends WebAppActivity { }
|
||||
public static class WebApp5 extends WebAppActivity { }
|
||||
public static class WebApp6 extends WebAppActivity { }
|
||||
public static class WebApp7 extends WebAppActivity { }
|
||||
public static class WebApp8 extends WebAppActivity { }
|
||||
public static class WebApp9 extends WebAppActivity { }
|
||||
}
|
|
@ -792,6 +792,8 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
|
|||
'util/TouchTargetUtil.java',
|
||||
'util/ViewUtil.java',
|
||||
'webapps/WebAppActivity.java',
|
||||
'webapps/WebAppIndexer.java',
|
||||
'webapps/WebApps.java',
|
||||
'widget/ActivityChooserModel.java',
|
||||
'widget/AllCapsTextView.java',
|
||||
'widget/AnchoredPopup.java',
|
||||
|
|
Загрузка…
Ссылка в новой задаче