Bug 1369393 - Backout GV-based custom tabs, PWAs, and Tabs.java changes. r=me

This commit is contained in:
Dylan Roeh 2017-06-01 09:28:31 -05:00
parent 881795c074
commit 093f070363
10 changed files with 374 additions and 276 deletions

View File

@ -1418,7 +1418,7 @@ public abstract class GeckoApp extends GeckoActivity
"ToggleChrome:Show",
null);
Tabs.getInstance().attachToContext(this, mLayerView, getAppEventDispatcher());
Tabs.getInstance().attachToContext(this, mLayerView);
Tabs.registerOnTabsChangedListener(this);
// Use global layout state change to kick off additional initialization
@ -1512,7 +1512,7 @@ public abstract class GeckoApp extends GeckoActivity
// If we are doing a restore, send the parsed session data to Gecko.
if (!mIsRestoringActivity) {
getAppEventDispatcher().dispatch("Session:Restore", restoreMessage);
EventDispatcher.getInstance().dispatch("Session:Restore", restoreMessage);
}
// Make sure sessionstore.old is either updated or deleted as necessary.
@ -2489,7 +2489,6 @@ public abstract class GeckoApp extends GeckoActivity
super.onDestroy();
Tabs.unregisterOnTabsChangedListener(this);
Tabs.getInstance().detachFromContext();
if (mShutdownOnDestroy) {
GeckoApplication.shutdown(!mRestartOnShutdown ? null : new Intent(

View File

@ -182,7 +182,7 @@ class GlobalHistory {
/* protected */ void checkVisited(final String uri) {
final String storedURI = ReaderModeUtils.stripAboutReaderUrl(uri);
final NotifierRunnable runnable = new NotifierRunnable(GeckoAppShell.getApplicationContext());
final NotifierRunnable runnable = new NotifierRunnable(GeckoAppShell.getContext());
mHandler.post(new Runnable() {
@Override
public void run() {

View File

@ -101,7 +101,6 @@ public class Tabs implements BundleEventListener {
private volatile boolean mInitialTabsAdded;
private Context mAppContext;
private EventDispatcher mEventDispatcher;
private LayerView mLayerView;
private ContentObserver mBookmarksContentObserver;
private PersistTabsRunnable mPersistTabsRunnable;
@ -166,11 +165,18 @@ public class Tabs implements BundleEventListener {
mPrivateClearColor = Color.RED;
}
public synchronized void attachToContext(Context context, LayerView layerView, EventDispatcher eventDispatcher) {
public synchronized void attachToContext(Context context, LayerView layerView) {
final Context appContext = context.getApplicationContext();
if (mAppContext == appContext) {
return;
}
if (mAppContext != null) {
// This should never happen.
Log.w(LOGTAG, "The application context has changed!");
}
mAppContext = appContext;
mEventDispatcher = eventDispatcher;
mLayerView = layerView;
mPrivateClearColor = ContextCompat.getColor(context, R.color.tabs_tray_grey_pressed);
mAccountManager = AccountManager.get(appContext);
@ -192,10 +198,6 @@ public class Tabs implements BundleEventListener {
}
}
public void detachFromContext() {
mLayerView = null;
}
/**
* Gets the tab count corresponding to the category and private state of the
* selected tab.
@ -330,7 +332,6 @@ public class Tabs implements BundleEventListener {
// Pass a message to Gecko to update tab state in BrowserApp.
final GeckoBundle data = new GeckoBundle(1);
data.putInt("id", tab.getId());
mEventDispatcher.dispatch("Tab:Selected", data);
EventDispatcher.getInstance().dispatch("Tab:Selected", data);
return tab;
}
@ -472,7 +473,7 @@ public class Tabs implements BundleEventListener {
final GeckoBundle data = new GeckoBundle(2);
data.putInt("tabId", tabId);
data.putBoolean("showUndoToast", showUndoToast);
mEventDispatcher.dispatch("Tab:Closed", data);
EventDispatcher.getInstance().dispatch("Tab:Closed", data);
}
/** Return the tab that will be selected by default after this one is closed */
@ -1082,7 +1083,7 @@ public class Tabs implements BundleEventListener {
}
}
mEventDispatcher.dispatch("Tab:Load", data);
EventDispatcher.getInstance().dispatch("Tab:Load", data);
if (tabToSelect == null) {
return null;
@ -1276,7 +1277,7 @@ public class Tabs implements BundleEventListener {
data.putInt("fromPosition", fromPosition);
data.putInt("toTabId", toTabId);
data.putInt("toPosition", toPosition);
mEventDispatcher.dispatch("Tab:Move", data);
EventDispatcher.getInstance().dispatch("Tab:Move", data);
}
/**

View File

@ -30,7 +30,6 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import org.mozilla.gecko.GeckoView;
import org.mozilla.gecko.R;
import org.mozilla.gecko.SiteIdentity;
import org.mozilla.gecko.Tab;
@ -109,24 +108,25 @@ public class ActionBarPresenter {
* @param url Url String to display
*/
public void displayUrlOnly(@NonNull final String url) {
updateCustomView(null, url, GeckoView.ProgressListener.STATE_IS_INSECURE);
updateCustomView(null, null, url);
}
/**
* Update appearance of CustomView of ActionBar
* Update appearance of CustomView of ActionBar.
*
* @param title Title for current website. Could be null if don't want to show title.
* @param url URL for current website. At least Custom will show this url.
* @param securityStatus Security status, possible values given in GeckoView.ProgressListener
* @param tab a Tab instance of current web page to provide information to render ActionBar.
*/
public void update(final String title, final String url, final int securityStatus) {
public void update(@NonNull final Tab tab) {
final String title = tab.getTitle();
final String url = tab.getBaseDomain();
// Do not update CustomView immediately. If this method be invoked rapidly several times,
// only apply last one.
mHandler.removeCallbacks(mUpdateAction);
mUpdateAction = new Runnable() {
@Override
public void run() {
updateCustomView(title, url, securityStatus);
updateCustomView(tab.getSiteIdentity(), title, url);
}
};
mHandler.postDelayed(mUpdateAction, CUSTOM_VIEW_UPDATE_DELAY);
@ -220,15 +220,25 @@ public class ActionBarPresenter {
* @param url URL for current website. At least Custom will show this url.
*/
@UiThread
private void updateCustomView(final String title, final String url, final int securityStatus) {
if (securityStatus == GeckoView.ProgressListener.STATE_IS_SECURE) {
mIconView.setVisibility(View.VISIBLE);
mIconView.setImageLevel(SecurityModeUtil.Mode.LOCK_SECURE.ordinal());
// Lock-Secure is special case. Keep its original green color.
DrawableCompat.setTintList(mIconView.getDrawable(), null);
} else {
private void updateCustomView(@Nullable SiteIdentity identity,
@Nullable String title,
@NonNull String url) {
// update site-info icon
if (identity == null) {
mIconView.setVisibility(View.INVISIBLE);
DrawableCompat.setTint(mIconView.getDrawable(), mTextPrimaryColor);
} else {
final SecurityModeUtil.Mode mode = SecurityModeUtil.resolve(identity);
mIconView.setVisibility(View.VISIBLE);
mIconView.setImageLevel(mode.ordinal());
mIdentityPopup.setSiteIdentity(identity);
if (mode == SecurityModeUtil.Mode.LOCK_SECURE) {
// Lock-Secure is special case. Keep its original green color.
DrawableCompat.setTintList(mIconView.getDrawable(), null);
} else {
// Icon use same color as TextView.
DrawableCompat.setTint(mIconView.getDrawable(), mTextPrimaryColor);
}
}
// If no title to use, use Url as title

View File

@ -20,7 +20,7 @@ import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.util.SparseArrayCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
@ -28,14 +28,17 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoView;
import org.mozilla.gecko.GeckoViewSettings;
import org.mozilla.gecko.R;
import org.mozilla.gecko.SingleTabActivity;
import org.mozilla.gecko.SnackbarBuilder;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.GeckoMenuInflater;
import org.mozilla.gecko.mozglue.SafeIntent;
@ -43,15 +46,14 @@ import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.ColorUtil;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.IntentUtils;
import org.mozilla.gecko.widget.ActionModePresenter;
import org.mozilla.gecko.widget.GeckoPopupMenu;
import java.util.List;
public class CustomTabsActivity extends AppCompatActivity
implements GeckoMenu.Callback,
GeckoView.ContentListener,
GeckoView.NavigationListener,
GeckoView.ProgressListener {
import static org.mozilla.gecko.Tabs.TabEvents;
public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabsChangedListener {
private static final String LOGTAG = "CustomTabsActivity";
@ -59,30 +61,21 @@ public class CustomTabsActivity extends AppCompatActivity
private GeckoPopupMenu popupMenu;
private View doorhangerOverlay;
private ActionBarPresenter actionBarPresenter;
private ProgressBar mProgressView;
// A state to indicate whether this activity is finishing with customize animation
private boolean usingCustomAnimation = false;
private MenuItem menuItemControl;
private GeckoView mGeckoView;
private boolean mCanGoBack = false;
private boolean mCanGoForward = false;
private boolean mCanStop = false;
private String mCurrentUrl;
private String mCurrentTitle;
private int mSecurityStatus = GeckoView.ProgressListener.STATE_IS_INSECURE;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.customtabs_activity);
final SafeIntent intent = new SafeIntent(getIntent());
doorhangerOverlay = findViewById(R.id.custom_tabs_doorhanger_overlay);
mProgressView = (ProgressBar) findViewById(R.id.page_progress);
final Toolbar toolbar = (Toolbar) findViewById(R.id.actionbar);
setSupportActionBar(toolbar);
final ActionBar actionBar = getSupportActionBar();
@ -92,22 +85,26 @@ public class CustomTabsActivity extends AppCompatActivity
actionBarPresenter.displayUrlOnly(intent.getDataString());
actionBarPresenter.setBackgroundColor(IntentUtil.getToolbarColor(intent), getWindow());
actionBarPresenter.setTextLongClickListener(new UrlCopyListener());
}
mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
@Override
protected void onTabOpenFromIntent(Tab tab) {
super.onTabOpenFromIntent(tab);
mGeckoView.setNavigationListener(this);
mGeckoView.setProgressListener(this);
mGeckoView.setContentListener(this);
final String host = getReferrerHost();
recordCustomTabUsage(host);
sendTelemetry();
}
final GeckoViewSettings settings = mGeckoView.getSettings();
settings.setBoolean(GeckoViewSettings.USE_MULTIPROCESS, false);
@Override
protected void onTabSelectFromIntent(Tab tab) {
super.onTabSelectFromIntent(tab);
if (intent != null && !TextUtils.isEmpty(intent.getDataString())) {
mGeckoView.loadUri(intent.getDataString());
} else {
Log.w(LOGTAG, "No intend found for custom tab");
finish();
}
// We already listen for SELECTED events, but if the activity has been destroyed and
// subsequently recreated without a different tab having been selected in Gecko in the
// meantime, our startup won't trigger a SELECTED event because the selected tab in Gecko
// doesn't actually change.
actionBarPresenter.update(tab);
}
private void sendTelemetry() {
@ -157,6 +154,13 @@ public class CustomTabsActivity extends AppCompatActivity
}
}
@Override
public void onDone() {
// We're most probably running within a foreign app's task, so we have no choice what to
// call here if we want to allow the user to return to that task's previous activity.
finish();
}
@Override
public void finish() {
super.finish();
@ -172,12 +176,63 @@ public class CustomTabsActivity extends AppCompatActivity
}
@Override
public void onBackPressed() {
if (mCanGoBack) {
mGeckoView.goBack();
} else {
finish();
protected int getNewTabFlags() {
return Tabs.LOADURL_CUSTOMTAB | super.getNewTabFlags();
}
@Override
public int getLayout() {
return R.layout.customtabs_activity;
}
@Override
public View getDoorhangerOverlay() {
return doorhangerOverlay;
}
@Override
public void onTabChanged(Tab tab, TabEvents msg, String data) {
super.onTabChanged(tab, msg, data);
if (!Tabs.getInstance().isSelectedTab(tab) ||
tab.getType() != Tab.TabType.CUSTOMTAB) {
return;
}
if (msg == TabEvents.START
|| msg == TabEvents.STOP
|| msg == TabEvents.ADDED
|| msg == TabEvents.LOAD_ERROR
|| msg == TabEvents.LOADED
|| msg == TabEvents.LOCATION_CHANGE
|| msg == TabEvents.SELECTED) {
updateProgress((tab.getState() == Tab.STATE_LOADING),
tab.getLoadProgress());
}
if (msg == TabEvents.LOCATION_CHANGE
|| msg == TabEvents.SECURITY_CHANGE
|| msg == TabEvents.TITLE
|| msg == TabEvents.SELECTED) {
actionBarPresenter.update(tab);
}
updateMenuItemForward();
}
@Override
public void onResume() {
super.onResume();
mLayerView.getDynamicToolbarAnimator().setPinned(true, PinReason.CUSTOM_TAB);
actionBarPresenter.onResume();
}
@Override
public void onPause() {
super.onPause();
mLayerView.getDynamicToolbarAnimator().setPinned(false, PinReason.CUSTOM_TAB);
actionBarPresenter.onPause();
}
// Usually should use onCreateOptionsMenu() to initialize menu items. But GeckoApp overwrite
@ -185,7 +240,7 @@ public class CustomTabsActivity extends AppCompatActivity
// and this.onPrepareOptionsMenu() are different instances - GeckoApp.onCreatePanelMenu() changed it.
// CustomTabsActivity only use standard menu in ActionBar, so initialize menu here.
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
public boolean onCreatePanelMenu(final int id, final Menu menu) {
// if 3rd-party app asks to add an action button
SafeIntent intent = new SafeIntent(getIntent());
@ -220,16 +275,6 @@ public class CustomTabsActivity extends AppCompatActivity
return true;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
return onOptionsItemSelected(item);
}
@Override
public boolean onMenuItemLongClick(MenuItem item) {
return false;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@ -272,11 +317,34 @@ public class CustomTabsActivity extends AppCompatActivity
performPendingIntent(intent);
}
@Override
protected ActionModePresenter getTextSelectPresenter() {
return new ActionModePresenter() {
private ActionMode mMode;
@Override
public void startActionMode(ActionMode.Callback callback) {
mMode = startSupportActionMode(callback);
}
@Override
public void endActionMode() {
if (mMode != null) {
mMode.finish();
}
}
};
}
private void bindNavigationCallback(@NonNull final Toolbar toolbar) {
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
onDone();
final Tabs tabs = Tabs.getInstance();
final Tab tab = tabs.getSelectedTab();
mSuppressActivitySwitch = true;
tabs.closeTab(tab);
}
});
}
@ -284,7 +352,8 @@ public class CustomTabsActivity extends AppCompatActivity
private void performPendingIntent(@NonNull PendingIntent pendingIntent) {
// bug 1337771: If intent-creator haven't set data url, call send() directly won't work.
final Intent additional = new Intent();
additional.setData(Uri.parse(mCurrentUrl));
final Tab tab = Tabs.getInstance().getSelectedTab();
additional.setData(Uri.parse(tab.getURL()));
try {
pendingIntent.send(this, 0, additional);
} catch (PendingIntent.CanceledException e) {
@ -366,45 +435,49 @@ public class CustomTabsActivity extends AppCompatActivity
}
final MenuItem forwardMenuItem = popupMenu.getMenu().findItem(R.id.custom_tabs_menu_forward);
forwardMenuItem.setEnabled(mCanGoForward);
final Tab tab = Tabs.getInstance().getSelectedTab();
final boolean enabled = (tab != null && tab.canDoForward());
forwardMenuItem.setEnabled(enabled);
}
/**
* Update loading status of current page
* Update loading progress of current page
*
* @param isLoading to indicate whether ProgressBar should be visible or not
* @param progress value of loading progress in percent, should be 0 - 100.
*/
private void updateCanStop() {
private void updateProgress(final boolean isLoading, final int progress) {
if (isLoading) {
mProgressView.setVisibility(View.VISIBLE);
mProgressView.setProgress(progress);
} else {
mProgressView.setVisibility(View.GONE);
}
if (menuItemControl != null) {
Drawable icon = menuItemControl.getIcon();
if (mCanStop) {
icon.setLevel(0);
} else {
icon.setLevel(100);
}
icon.setLevel(progress);
}
}
/**
* Update the state of the action bar
*/
private void updateActionBar() {
actionBarPresenter.update(mCurrentTitle, mCurrentUrl, mSecurityStatus);
}
/**
* Call this method to reload page, or stop page loading if progress not complete yet.
*/
private void onLoadingControlClicked() {
if (mCanStop) {
// TODO: enable this after implementing GeckoView.stop()
//mGeckoView.stop();
} else {
mGeckoView.reload();
final Tab tab = Tabs.getInstance().getSelectedTab();
if (tab != null) {
if (tab.getLoadProgress() == Tab.LOAD_PROGRESS_STOP) {
tab.doReload(true);
} else {
tab.doStop();
}
}
}
private void onForwardClicked() {
if (mCanGoForward) {
mGeckoView.goForward();
final Tab tab = Tabs.getInstance().getSelectedTab();
if ((tab != null) && tab.canDoForward()) {
tab.doForward();
}
}
@ -412,11 +485,15 @@ public class CustomTabsActivity extends AppCompatActivity
* Callback for Open-in menu item.
*/
private void onOpenInClicked() {
final Intent intent = new Intent();
intent.setData(Uri.parse(mCurrentUrl));
intent.setAction(Intent.ACTION_VIEW);
startActivity(intent);
finish();
final Tab tab = Tabs.getInstance().getSelectedTab();
if (tab != null) {
// To launch default browser with url of current tab.
final Intent intent = new Intent();
intent.setData(Uri.parse(tab.getURL()));
intent.setAction(Intent.ACTION_VIEW);
startActivity(intent);
finish();
}
}
private void onActionButtonClicked() {
@ -430,10 +507,12 @@ public class CustomTabsActivity extends AppCompatActivity
* Callback for Share menu item.
*/
private void onShareClicked() {
if (!TextUtils.isEmpty(mCurrentUrl)) {
final String url = Tabs.getInstance().getSelectedTab().getURL();
if (!TextUtils.isEmpty(url)) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, mCurrentUrl);
shareIntent.putExtra(Intent.EXTRA_TEXT, url);
Intent chooserIntent = Intent.createChooser(shareIntent, getString(R.string.share_title));
chooserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@ -447,8 +526,9 @@ public class CustomTabsActivity extends AppCompatActivity
private class UrlCopyListener implements View.OnLongClickListener {
@Override
public boolean onLongClick(View v) {
if (!TextUtils.isEmpty(mCurrentUrl)) {
Clipboard.setText(mCurrentUrl);
final String url = Tabs.getInstance().getSelectedTab().getURL();
if (!TextUtils.isEmpty(url)) {
Clipboard.setText(url);
SnackbarBuilder.builder(CustomTabsActivity.this)
.message(R.string.custom_tabs_hint_url_copy)
.duration(Snackbar.LENGTH_SHORT)
@ -474,59 +554,4 @@ public class CustomTabsActivity extends AppCompatActivity
}
return null;
}
/* GeckoView.NavigationListener */
@Override
public void onLocationChange(GeckoView view, String url) {
mCurrentUrl = url;
updateActionBar();
}
@Override
public void onCanGoBack(GeckoView view, boolean canGoBack) {
mCanGoBack = canGoBack;
}
@Override
public void onCanGoForward(GeckoView view, boolean canGoForward) {
mCanGoForward = canGoForward;
updateMenuItemForward();
}
/* GeckoView.ProgressListener */
@Override
public void onPageStart(GeckoView view, String url) {
mCurrentUrl = url;
mCanStop = true;
updateActionBar();
updateCanStop();
}
@Override
public void onPageStop(GeckoView view, boolean success) {
mCanStop = false;
updateCanStop();
}
@Override
public void onSecurityChange(GeckoView view, int status) {
if ((status & STATE_IS_INSECURE) != 0) {
mSecurityStatus = STATE_IS_INSECURE;
} else if ((status & STATE_IS_BROKEN) != 0) {
mSecurityStatus = STATE_IS_BROKEN;
} else if ((status & STATE_IS_SECURE) != 0) {
mSecurityStatus = STATE_IS_SECURE;
}
updateActionBar();
}
/* GeckoView.ContentListener */
@Override
public void onTitleChange(GeckoView view, String title) {
mCurrentTitle = title;
updateActionBar();
}
@Override
public void onFullScreen(GeckoView view, boolean fullScreen) {}
}

View File

@ -7,17 +7,15 @@ package org.mozilla.gecko.webapps;
import java.io.File;
import java.io.IOException;
import java.util.List;
import android.app.ActivityManager;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.Toolbar;
import android.support.v7.app.ActionBar;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@ -32,8 +30,6 @@ import org.json.JSONException;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoView;
import org.mozilla.gecko.GeckoViewSettings;
import org.mozilla.gecko.SingleTabActivity;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
@ -52,22 +48,20 @@ import org.mozilla.gecko.widget.AnchoredPopup;
import static org.mozilla.gecko.Tabs.TabEvents;
public class WebAppActivity extends AppCompatActivity
implements GeckoView.NavigationListener {
public class WebAppActivity extends SingleTabActivity {
private static final String LOGTAG = "WebAppActivity";
public static final String MANIFEST_PATH = "MANIFEST_PATH";
private static final String SAVED_INTENT = "savedIntent";
private TextView mUrlView;
private GeckoView mGeckoView;
private View doorhangerOverlay;
private Uri mScope;
@Override
public void onCreate(Bundle savedInstanceState) {
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0 &&
savedInstanceState != null) {
savedInstanceState != null) {
// Even though we're a single task activity, Android's task switcher has the
// annoying habit of never updating its stored intent after our initial creation,
// even if we've been subsequently started with a new intent.
@ -81,33 +75,73 @@ public class WebAppActivity extends AppCompatActivity
super.onCreate(savedInstanceState);
setContentView(R.layout.customtabs_activity);
final Toolbar toolbar = (Toolbar) findViewById(R.id.actionbar);
setSupportActionBar(toolbar);
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.page_progress);
progressBar.setVisibility(View.GONE);
final ActionBar actionBar = getSupportActionBar();
actionBar.setCustomView(R.layout.webapps_action_bar_custom_view);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.hide();
doorhangerOverlay = findViewById(R.id.custom_tabs_doorhanger_overlay);
final View customView = actionBar.getCustomView();
mUrlView = (TextView) customView.findViewById(R.id.webapps_action_bar_url);
mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
EventDispatcher.getInstance().registerUiThreadListener(this,
"Website:AppEntered",
"Website:AppLeft",
null);
}
mGeckoView.setNavigationListener(this);
@Override
public View getDoorhangerOverlay() {
return doorhangerOverlay;
}
final GeckoViewSettings settings = mGeckoView.getSettings();
settings.setBoolean(GeckoViewSettings.USE_MULTIPROCESS, false);
@Override
public int getLayout() {
return R.layout.customtabs_activity;
}
final Uri u = getIntent().getData();
if (u != null) {
mGeckoView.loadUri(u.toString());
@Override
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
super.handleMessage(event, message, callback);
if (message == null ||
!message.containsKey("tabId") || message.getInt("tabId") != mLastSelectedTabId) {
return;
}
loadManifest(getIntent().getStringExtra(MANIFEST_PATH));
switch (event) {
case "Website:AppEntered":
getSupportActionBar().hide();
break;
case "Website:AppLeft":
getSupportActionBar().show();
break;
}
}
@Override
public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
super.onTabChanged(tab, msg, data);
if (tab == null || !Tabs.getInstance().isSelectedTab(tab) ||
tab.getType() != Tab.TabType.WEBAPP) {
return;
}
if (msg == TabEvents.LOCATION_CHANGE ||
msg == TabEvents.SELECTED) {
mUrlView.setText(tab.getURL());
}
}
@Override
@ -117,6 +151,75 @@ public class WebAppActivity extends AppCompatActivity
outState.putParcelable(SAVED_INTENT, getIntent());
}
@Override
public void onDestroy() {
super.onDestroy();
EventDispatcher.getInstance().unregisterUiThreadListener(this,
"Website:AppEntered",
"Website:AppLeft",
null);
}
@Override
protected int getNewTabFlags() {
return Tabs.LOADURL_WEBAPP | super.getNewTabFlags();
}
@Override
protected void onTabOpenFromIntent(Tab tab) {
super.onTabOpenFromIntent(tab);
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "webapp");
loadManifest(tab.getManifestPath());
}
/**
* In case this activity and its tab are reused (the user has opened
* > 10 current web apps), we check that app launched is still within
* the same host as the intent has set.
* If it isn't, we reload the intent URL.
*/
@Override
protected void onTabSelectFromIntent(Tab tab) {
super.onTabSelectFromIntent(tab);
SafeIntent intent = new SafeIntent(getIntent());
final String launchUrl = intent.getDataString();
final String currentUrl = tab.getURL();
final boolean isSameDomain = Uri.parse(currentUrl).getHost()
.equals(Uri.parse(launchUrl).getHost());
final String manifestPath;
if (!isSameDomain) {
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "webapp");
manifestPath = intent.getStringExtra(MANIFEST_PATH);
tab.setManifestUrl(manifestPath);
Tabs.getInstance().loadUrl(launchUrl);
} else {
manifestPath = tab.getManifestPath();
}
loadManifest(manifestPath);
}
@Override
protected ActionModePresenter getTextSelectPresenter() {
return new ActionModePresenter() {
private ActionMode mMode;
@Override
public void startActionMode(ActionMode.Callback callback) {
mMode = startSupportActionMode(callback);
}
@Override
public void endActionMode() {
if (mMode != null) {
mMode.finish();
}
}
};
}
private void loadManifest(String manifestPath) {
if (TextUtils.isEmpty(manifestPath)) {
Log.e(LOGTAG, "Missing manifest");
@ -134,7 +237,6 @@ public class WebAppActivity extends AppCompatActivity
final Integer color = readColorFromManifest(manifestField);
final String name = readNameFromManifest(manifestField);
final Bitmap icon = readIconFromManifest(manifest);
mScope = readScopeFromManifest(manifest, manifestPath);
final ActivityManager.TaskDescription taskDescription = (color == null)
? new ActivityManager.TaskDescription(name, icon)
: new ActivityManager.TaskDescription(name, icon, color);
@ -180,76 +282,10 @@ public class WebAppActivity extends AppCompatActivity
return null;
}
final LoadFaviconResult loadIconResult = FaviconDecoder
.decodeDataURI(this, iconStr);
.decodeDataURI(getContext(), iconStr);
if (loadIconResult == null) {
return null;
}
return loadIconResult.getBestBitmap(GeckoAppShell.getPreferredIconSize());
}
private Uri readScopeFromManifest(JSONObject manifest, String manifestPath) {
final String scopeStr = manifest.optString("scope", null);
if (scopeStr == null) {
return null;
}
Uri res = Uri.parse(scopeStr);
if (res.isRelative()) {
// TODO: Handle this more correctly.
return null;
}
return res;
}
private boolean isInScope(String url) {
if (mScope == null) {
return true;
}
final Uri uri = Uri.parse(url);
if (!uri.getScheme().equals(mScope.getScheme())) {
return false;
}
if (!uri.getHost().equals(mScope.getHost())) {
return false;
}
final List<String> scopeSegments = mScope.getPathSegments();
final List<String> urlSegments = uri.getPathSegments();
if (scopeSegments.size() > urlSegments.size()) {
return false;
}
for (int i = 0; i < scopeSegments.size(); i++) {
if (!scopeSegments.get(i).equals(urlSegments.get(i))) {
return false;
}
}
return true;
}
/* GeckoView.NavigationListener */
@Override
public void onLocationChange(GeckoView view, String url) {
if (isInScope(url)) {
getSupportActionBar().hide();
} else {
getSupportActionBar().show();
}
mUrlView.setText(url);
}
@Override
public void onCanGoBack(GeckoView view, boolean canGoBack) {
}
@Override
public void onCanGoForward(GeckoView view, boolean canGoForward) {
}
}

View File

@ -12,6 +12,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--
This layout is quite complex because GeckoApp accesses all view groups
in this tree. In a perfect world this should just include a GeckoView.
-->
<android.support.v7.widget.Toolbar
android:id="@id/actionbar"
android:layout_width="match_parent"
@ -20,18 +25,48 @@
android:background="@color/text_and_tabs_tray_grey"
app:layout_scrollFlags="scroll|enterAlways"/>
<org.mozilla.gecko.GeckoView
android:id="@+id/gecko_view"
android:layout_width="fill_parent"
<view class="org.mozilla.gecko.GeckoApp$MainLayout"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_below="@id/actionbar"
android:layout_height="match_parent"
android:scrollbars="none"/>
android:background="@android:color/transparent">
<RelativeLayout android:id="@+id/gecko_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tablet_tab_strip"
android:layout_above="@+id/find_in_page">
<fragment class="org.mozilla.gecko.GeckoViewFragment"
android:id="@+id/layer_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"/>
<org.mozilla.gecko.FormAssistPopup android:id="@+id/form_assist_popup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</RelativeLayout>
</view>
<ProgressBar
android:id="@id/page_progress"
style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_alignTop="@id/main_layout"
android:background="@drawable/url_bar_bg"
android:progressDrawable="@drawable/progressbar"
tools:progress="70"/>
<View android:id="@+id/custom_tabs_doorhanger_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/dark_transparent_overlay"
android:alpha="0"
android:layerType="hardware"/>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/dark_transparent_overlay"
android:alpha="0"
android:layerType="hardware"/>
</RelativeLayout>

View File

@ -87,7 +87,7 @@ var FindHelper = {
this._initialViewport = JSON.stringify(this._targetTab.getViewport());
this._viewportChanged = false;
WindowEventDispatcher.registerListener(this, [
GlobalEventDispatcher.registerListener(this, [
"Tab:Selected",
]);
},
@ -109,7 +109,7 @@ var FindHelper = {
this._initialViewport = null;
this._viewportChanged = false;
WindowEventDispatcher.unregisterListener(this, [
GlobalEventDispatcher.unregisterListener(this, [
"Tab:Selected",
]);
},

View File

@ -389,15 +389,11 @@ var BrowserApp = {
Services.androidBridge.browserApp = this;
WindowEventDispatcher.registerListener(this, [
"Session:Restore",
GlobalEventDispatcher.registerListener(this, [
"Tab:Load",
"Tab:Selected",
"Tab:Closed",
"Tab:Move",
]);
GlobalEventDispatcher.registerListener(this, [
"Browser:LoadManifest",
"Browser:Quit",
"Fonts:Reload",
@ -1855,10 +1851,6 @@ var BrowserApp = {
break;
}
case "Session:Restore":
GlobalEventDispatcher.dispatch("Session:Restore", data);
break;
case "Session:Stop":
browser.stop();
break;

View File

@ -7,7 +7,7 @@
<window id="main-window"
onload="startup();"
windowtype="navigator:geckoview"
windowtype="navigator:browser"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<browser id="content" type="content" primary="true" src="about:blank" flex="1"/>