Bug 1356346 - Overhaul CustomTabsActivity.java to use GeckoView rather than GeckoApp. r=jchen, walkingice

This commit is contained in:
Dylan Roeh 2017-04-25 11:48:05 -05:00
Родитель 3ee02fbbea
Коммит 7c456e1289
5 изменённых файлов: 163 добавлений и 233 удалений

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

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

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

@ -30,6 +30,7 @@ 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;
@ -108,25 +109,24 @@ public class ActionBarPresenter {
* @param url Url String to display
*/
public void displayUrlOnly(@NonNull final String url) {
updateCustomView(null, null, url);
updateCustomView(null, url, GeckoView.ProgressListener.STATE_IS_INSECURE);
}
/**
* Update appearance of CustomView of ActionBar.
* Update appearance of CustomView of ActionBar
*
* @param tab a Tab instance of current web page to provide information to render 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
*/
public void update(@NonNull final Tab tab) {
final String title = tab.getTitle();
final String url = tab.getBaseDomain();
public void update(final String title, final String url, final int securityStatus) {
// 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(tab.getSiteIdentity(), title, url);
updateCustomView(title, url, securityStatus);
}
};
mHandler.postDelayed(mUpdateAction, CUSTOM_VIEW_UPDATE_DELAY);
@ -220,25 +220,15 @@ public class ActionBarPresenter {
* @param url URL for current website. At least Custom will show this url.
*/
@UiThread
private void updateCustomView(@Nullable SiteIdentity identity,
@Nullable String title,
@NonNull String url) {
// update site-info icon
if (identity == null) {
mIconView.setVisibility(View.INVISIBLE);
} else {
final SecurityModeUtil.Mode mode = SecurityModeUtil.resolve(identity);
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(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);
}
mIconView.setImageLevel(SecurityModeUtil.Mode.LOCK_SECURE.ordinal());
// Lock-Secure is special case. Keep its original green color.
DrawableCompat.setTintList(mIconView.getDrawable(), null);
} else {
mIconView.setVisibility(View.INVISIBLE);
DrawableCompat.setTint(mIconView.getDrawable(), mTextPrimaryColor);
}
// If no title to use, use Url as title

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

@ -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.view.ActionMode;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
@ -28,17 +28,14 @@ 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;
@ -46,14 +43,15 @@ 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;
import static org.mozilla.gecko.Tabs.TabEvents;
public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabsChangedListener {
public class CustomTabsActivity extends AppCompatActivity
implements GeckoMenu.Callback,
GeckoView.ContentListener,
GeckoView.NavigationListener,
GeckoView.ProgressListener {
private static final String LOGTAG = "CustomTabsActivity";
@ -61,21 +59,30 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
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();
@ -85,26 +92,22 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
actionBarPresenter.displayUrlOnly(intent.getDataString());
actionBarPresenter.setBackgroundColor(IntentUtil.getToolbarColor(intent), getWindow());
actionBarPresenter.setTextLongClickListener(new UrlCopyListener());
}
@Override
protected void onTabOpenFromIntent(Tab tab) {
super.onTabOpenFromIntent(tab);
mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
final String host = getReferrerHost();
recordCustomTabUsage(host);
sendTelemetry();
}
mGeckoView.setNavigationListener(this);
mGeckoView.setProgressListener(this);
mGeckoView.setContentListener(this);
@Override
protected void onTabSelectFromIntent(Tab tab) {
super.onTabSelectFromIntent(tab);
final GeckoViewSettings settings = mGeckoView.getSettings();
settings.setBoolean(GeckoViewSettings.USE_MULTIPROCESS, false);
// 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);
if (intent != null && !TextUtils.isEmpty(intent.getDataString())) {
mGeckoView.loadUri(intent.getDataString());
} else {
Log.w(LOGTAG, "No intend found for custom tab");
finish();
}
}
private void sendTelemetry() {
@ -154,13 +157,6 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
}
}
@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();
@ -176,63 +172,12 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
}
@Override
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;
public void onBackPressed() {
if (mCanGoBack) {
mGeckoView.goBack();
} else {
finish();
}
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
@ -240,7 +185,7 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
// and this.onPrepareOptionsMenu() are different instances - GeckoApp.onCreatePanelMenu() changed it.
// CustomTabsActivity only use standard menu in ActionBar, so initialize menu here.
@Override
public boolean onCreatePanelMenu(final int id, final Menu menu) {
public boolean onCreateOptionsMenu(final Menu menu) {
// if 3rd-party app asks to add an action button
SafeIntent intent = new SafeIntent(getIntent());
@ -275,6 +220,16 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
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()) {
@ -317,34 +272,11 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
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) {
onDone();
final Tabs tabs = Tabs.getInstance();
final Tab tab = tabs.getSelectedTab();
mSuppressActivitySwitch = true;
tabs.closeTab(tab);
finish();
}
});
}
@ -352,8 +284,7 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
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();
final Tab tab = Tabs.getInstance().getSelectedTab();
additional.setData(Uri.parse(tab.getURL()));
additional.setData(Uri.parse(mCurrentUrl));
try {
pendingIntent.send(this, 0, additional);
} catch (PendingIntent.CanceledException e) {
@ -435,49 +366,45 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
}
final MenuItem forwardMenuItem = popupMenu.getMenu().findItem(R.id.custom_tabs_menu_forward);
final Tab tab = Tabs.getInstance().getSelectedTab();
final boolean enabled = (tab != null && tab.canDoForward());
forwardMenuItem.setEnabled(enabled);
forwardMenuItem.setEnabled(mCanGoForward);
}
/**
* 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.
* Update loading status of current page
*/
private void updateProgress(final boolean isLoading, final int progress) {
if (isLoading) {
mProgressView.setVisibility(View.VISIBLE);
mProgressView.setProgress(progress);
} else {
mProgressView.setVisibility(View.GONE);
}
private void updateCanStop() {
if (menuItemControl != null) {
Drawable icon = menuItemControl.getIcon();
icon.setLevel(progress);
if (mCanStop) {
icon.setLevel(0);
} else {
icon.setLevel(100);
}
}
}
/**
* 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() {
final Tab tab = Tabs.getInstance().getSelectedTab();
if (tab != null) {
if (tab.getLoadProgress() == Tab.LOAD_PROGRESS_STOP) {
tab.doReload(true);
} else {
tab.doStop();
}
if (mCanStop) {
// TODO: enable this after implementing GeckoView.stop()
//mGeckoView.stop();
} else {
mGeckoView.reload();
}
}
private void onForwardClicked() {
final Tab tab = Tabs.getInstance().getSelectedTab();
if ((tab != null) && tab.canDoForward()) {
tab.doForward();
if (mCanGoForward) {
mGeckoView.goForward();
}
}
@ -485,15 +412,11 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
* Callback for Open-in menu item.
*/
private void onOpenInClicked() {
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();
}
final Intent intent = new Intent();
intent.setData(Uri.parse(mCurrentUrl));
intent.setAction(Intent.ACTION_VIEW);
startActivity(intent);
finish();
}
private void onActionButtonClicked() {
@ -507,12 +430,10 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
* Callback for Share menu item.
*/
private void onShareClicked() {
final String url = Tabs.getInstance().getSelectedTab().getURL();
if (!TextUtils.isEmpty(url)) {
if (!TextUtils.isEmpty(mCurrentUrl)) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, url);
shareIntent.putExtra(Intent.EXTRA_TEXT, mCurrentUrl);
Intent chooserIntent = Intent.createChooser(shareIntent, getString(R.string.share_title));
chooserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@ -526,9 +447,8 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
private class UrlCopyListener implements View.OnLongClickListener {
@Override
public boolean onLongClick(View v) {
final String url = Tabs.getInstance().getSelectedTab().getURL();
if (!TextUtils.isEmpty(url)) {
Clipboard.setText(url);
if (!TextUtils.isEmpty(mCurrentUrl)) {
Clipboard.setText(mCurrentUrl);
SnackbarBuilder.builder(CustomTabsActivity.this)
.message(R.string.custom_tabs_hint_url_copy)
.duration(Snackbar.LENGTH_SHORT)
@ -554,4 +474,59 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
}
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) {}
}

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

@ -12,11 +12,6 @@
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"
@ -25,48 +20,18 @@
android:background="@color/text_and_tabs_tray_grey"
app:layout_scrollFlags="scroll|enterAlways"/>
<view class="org.mozilla.gecko.GeckoApp$MainLayout"
android:id="@+id/main_layout"
android:layout_width="match_parent"
<org.mozilla.gecko.GeckoView
android:id="@+id/gecko_view"
android:layout_width="fill_parent"
android:layout_below="@id/actionbar"
android:layout_height="match_parent"
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"/>
android:scrollbars="none"/>
<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>

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

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