author | Julian_Chu <walkingice0204@gmail.com> |
Thu, 09 Mar 2017 15:17:36 +0800 | |
changeset 348315 | 540020bee3a69181f00d1ab059d6588f3c9a2cd8 |
parent 348314 | 4796c44dda66b89f59c9a40a85534061d7aed578 |
child 348316 | c9df402008ca91b48edcb5de1e8173f68c93108b |
push id | 88187 |
push user | archaeopteryx@coole-files.de |
push date | Sat, 18 Mar 2017 15:27:00 +0000 |
treeherder | mozilla-inbound@0b1d3324cffe [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | sebastian |
bugs | 1332546 |
milestone | 55.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/ActionBarPresenter.java +++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/ActionBarPresenter.java @@ -1,77 +1,102 @@ /* -*- 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.customtabs; +import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.drawable.ColorDrawable; import android.os.Build; +import android.os.Handler; import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; +import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.app.ActionBar; -import android.support.v7.widget.Toolbar; import android.text.TextUtils; -import android.util.Log; +import android.view.View; import android.view.Window; import android.view.WindowManager; +import android.widget.ImageButton; import android.widget.TextView; -import org.mozilla.gecko.AppConstants; +import org.mozilla.gecko.R; +import org.mozilla.gecko.SiteIdentity; +import org.mozilla.gecko.Tab; +import org.mozilla.gecko.toolbar.SecurityModeUtil; import org.mozilla.gecko.util.ColorUtil; -import java.lang.reflect.Field; - /** * This class is used to maintain appearance of ActionBar of CustomTabsActivity, includes background * color, custom-view and so on. */ public class ActionBarPresenter { - private static final String LOGTAG = "CustomTabsActionBar"; - private final ActionBar mActionBar; - private boolean useDomainTitle = true; + @ColorInt + private static final int DEFAULT_TEXT_PRIMARY_COLOR = 0xFFFFFFFF; + private static final long CUSTOM_VIEW_UPDATE_DELAY = 1000; - ActionBarPresenter(@NonNull final ActionBar actionBar, @NonNull Toolbar toolbar) { - mActionBar = actionBar; - initActionBar(toolbar); - } + private final ActionBar mActionBar; + private final ImageButton mIconView; + private final TextView mTitleView; + private final TextView mUrlView; + private final Handler mHandler = new Handler(); + + private Runnable mUpdateAction; - private void initActionBar(@NonNull final Toolbar toolbar) { - try { - // Since we don't create the Toolbar's TextView ourselves, this seems - // to be the only way of changing the ellipsize setting. - final Field f = toolbar.getClass().getDeclaredField("mTitleTextView"); - f.setAccessible(true); - final TextView textView = (TextView) f.get(toolbar); - textView.setEllipsize(TextUtils.TruncateAt.START); - } catch (Exception e) { - // If we can't ellipsize at the start of the title, we shouldn't display the host - // so as to avoid displaying a misleadingly truncated host. - Log.w(LOGTAG, "Failed to get Toolbar TextView, using default title."); - useDomainTitle = false; - } + @ColorInt + private int mTextPrimaryColor = DEFAULT_TEXT_PRIMARY_COLOR; + + ActionBarPresenter(@NonNull final ActionBar actionBar) { + mActionBar = actionBar; + mActionBar.setDisplayShowCustomEnabled(true); + mActionBar.setDisplayShowTitleEnabled(false); + + mActionBar.setCustomView(R.layout.customtabs_action_bar_custom_view); + final View customView = mActionBar.getCustomView(); + mIconView = (ImageButton) customView.findViewById(R.id.custom_tabs_action_bar_icon); + mTitleView = (TextView) customView.findViewById(R.id.custom_tabs_action_bar_title); + mUrlView = (TextView) customView.findViewById(R.id.custom_tabs_action_bar_url); + + onThemeChanged(mActionBar.getThemedContext().getTheme()); } /** - * Update appearance of ActionBar, includes its Title. + * To display Url in CustomView only and immediately. * - * @param title A string to be used as Title in Actionbar + * @param url Url String to display + */ + public void displayUrlOnly(@NonNull final String url) { + updateCustomView(null, null, url); + } + + /** + * Update appearance of CustomView of ActionBar. + * + * @param tab a Tab instance of current web page to provide information to render ActionBar. */ - @UiThread - public void update(@Nullable final String title) { - if (useDomainTitle || TextUtils.isEmpty(title)) { - mActionBar.setTitle(AppConstants.MOZ_APP_BASENAME); - } else { - mActionBar.setTitle(title); - } + 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(tab.getSiteIdentity(), title, url); + } + }; + mHandler.postDelayed(mUpdateAction, CUSTOM_VIEW_UPDATE_DELAY); } /** * Set background color to ActionBar, as well as Status bar. * * @param color the color to apply to ActionBar * @param window Window instance for changing color status bar, or null if won't change it. */ @@ -82,9 +107,58 @@ public class ActionBarPresenter { if (window != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(ColorUtil.darken(color, 0.25)); } } } + + /** + * To update appearance of CustomView of ActionBar, includes its icon, title and url text. + * + * @param identity SiteIdentity for current website. Could be null if don't want to show icon. + * @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. + */ + @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); + mIconView.setVisibility(View.VISIBLE); + mIconView.setImageLevel(mode.ordinal()); + + 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 + if (TextUtils.isEmpty(title)) { + mTitleView.setText(url); + mUrlView.setText(null); + mUrlView.setVisibility(View.GONE); + } else { + mTitleView.setText(title); + mUrlView.setText(url); + mUrlView.setVisibility(View.VISIBLE); + } + } + + private void onThemeChanged(@NonNull final Resources.Theme currentTheme) { + // Theme might be light or dark. To get text color for custom-view. + final TypedArray themeArray = currentTheme.obtainStyledAttributes( + new int[]{android.R.attr.textColorPrimary}); + + mTextPrimaryColor = themeArray.getColor(0, DEFAULT_TEXT_PRIMARY_COLOR); + themeArray.recycle(); + } }
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java +++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java @@ -26,75 +26,69 @@ import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.widget.ImageButton; -import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.R; import org.mozilla.gecko.Tab; import org.mozilla.gecko.Tabs; import org.mozilla.gecko.menu.GeckoMenu; import org.mozilla.gecko.menu.GeckoMenuInflater; import org.mozilla.gecko.util.ColorUtil; import org.mozilla.gecko.widget.GeckoPopupMenu; import java.util.List; import static android.support.customtabs.CustomTabsIntent.EXTRA_TOOLBAR_COLOR; public class CustomTabsActivity extends GeckoApp implements Tabs.OnTabsChangedListener { private static final String LOGTAG = "CustomTabsActivity"; private static final String SAVED_TOOLBAR_COLOR = "SavedToolbarColor"; - private static final String SAVED_TOOLBAR_TITLE = "SavedToolbarTitle"; @ColorInt private static final int DEFAULT_ACTION_BAR_COLOR = 0xFF363b40; // default color to match design private final SparseArrayCompat<PendingIntent> menuItemsIntent = new SparseArrayCompat<>(); private GeckoPopupMenu popupMenu; - private int tabId = -1; private ActionBarPresenter actionBarPresenter; - private String toolbarTitle; // A state to indicate whether this activity is finishing with customize animation private boolean usingCustomAnimation = false; @ColorInt private int toolbarColor = DEFAULT_ACTION_BAR_COLOR; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { toolbarColor = savedInstanceState.getInt(SAVED_TOOLBAR_COLOR, DEFAULT_ACTION_BAR_COLOR); - toolbarTitle = savedInstanceState.getString(SAVED_TOOLBAR_TITLE, AppConstants.MOZ_APP_BASENAME); } else { toolbarColor = getIntent().getIntExtra(EXTRA_TOOLBAR_COLOR, DEFAULT_ACTION_BAR_COLOR); - toolbarTitle = AppConstants.MOZ_APP_BASENAME; } // Translucent color does not make sense for toolbar color. Ensure it is 0xFF. toolbarColor = 0xFF000000 | toolbarColor; setThemeFromToolbarColor(); final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); bindNavigationCallback(toolbar); - actionBarPresenter = new ActionBarPresenter(actionBar, toolbar); + actionBarPresenter = new ActionBarPresenter(actionBar); + actionBarPresenter.displayUrlOnly(getIntent().getDataString()); actionBarPresenter.setBackgroundColor(toolbarColor, getWindow()); - actionBarPresenter.update(toolbarTitle); + actionBar.setDisplayHomeAsUpEnabled(true); Tabs.registerOnTabsChangedListener(this); } private void setThemeFromToolbarColor() { @StyleRes int styleRes = (ColorUtil.getReadableTextColor(toolbarColor) == Color.BLACK) ? R.style.GeckoCustomTabs_Light @@ -147,48 +141,34 @@ public class CustomTabsActivity extends @Override protected void onDone() { finish(); } @Override public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) { - if (tab == null) { - return; - } - - if (tabId >= 0 && tab.getId() != tabId) { + if (!Tabs.getInstance().isSelectedTab(tab)) { return; } - if (msg == Tabs.TabEvents.LOCATION_CHANGE) { - tabId = tab.getId(); - final Uri uri = Uri.parse(tab.getURL()); - String title = null; - if (uri != null) { - title = uri.getHost(); - } - if (title == null || title.isEmpty()) { - toolbarTitle = AppConstants.MOZ_APP_BASENAME; - } else { - toolbarTitle = title; - } - actionBarPresenter.update(toolbarTitle); + if (msg == Tabs.TabEvents.LOCATION_CHANGE + || msg == Tabs.TabEvents.SECURITY_CHANGE + || msg == Tabs.TabEvents.TITLE) { + actionBarPresenter.update(tab); } updateMenuItemForward(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(SAVED_TOOLBAR_COLOR, toolbarColor); - outState.putString(SAVED_TOOLBAR_TITLE, toolbarTitle); } @Override public void onResume() { if (lastSelectedTabId >= 0) { final Tabs tabs = Tabs.getInstance(); final Tab tab = tabs.getTab(lastSelectedTabId); if (tab == null) {
new file mode 100644 --- /dev/null +++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/SecurityModeUtil.java @@ -0,0 +1,84 @@ +/* -*- 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.toolbar; + +import android.support.annotation.Nullable; + +import org.mozilla.gecko.SiteIdentity; +import org.mozilla.gecko.SiteIdentity.MixedMode; +import org.mozilla.gecko.SiteIdentity.SecurityMode; +import org.mozilla.gecko.SiteIdentity.TrackingMode; + +import java.util.HashMap; +import java.util.Map; + +/** + * Util class which encapsulate logic of how CustomTabsActivity treats SiteIdentity. + * TODO: Bug 1347037 - This class should be reusable for other components + */ +public class SecurityModeUtil { + + // defined basic mapping between SecurityMode and SecurityModeUtil.Mode + private static final Map<SecurityMode, Mode> securityModeMap; + + static { + securityModeMap = new HashMap<>(); + securityModeMap.put(SecurityMode.UNKNOWN, Mode.UNKNOWN); + securityModeMap.put(SecurityMode.IDENTIFIED, Mode.LOCK_SECURE); + securityModeMap.put(SecurityMode.VERIFIED, Mode.LOCK_SECURE); + securityModeMap.put(SecurityMode.CHROMEUI, Mode.UNKNOWN); + } + + /** + * To resolve which mode to be used for given SiteIdentity. Its logic is similar to + * ToolbarDisplayLayout.updateSiteIdentity + * + * @param identity An identity of a site to be resolved + * @return Corresponding mode for resolved SiteIdentity, UNKNOWN as default. + */ + public static Mode resolve(final @Nullable SiteIdentity identity) { + if (identity == null) { + return Mode.UNKNOWN; + } + + final SecurityMode securityMode = identity.getSecurityMode(); + final MixedMode activeMixedMode = identity.getMixedModeActive(); + final MixedMode displayMixedMode = identity.getMixedModeDisplay(); + final TrackingMode trackingMode = identity.getTrackingMode(); + final boolean securityException = identity.isSecurityException(); + + if (securityMode == SiteIdentity.SecurityMode.CHROMEUI) { + return Mode.UNKNOWN; + } + + if (securityException) { + return Mode.MIXED_MODE; + } else if (trackingMode == TrackingMode.TRACKING_CONTENT_LOADED) { + return Mode.TRACKING_CONTENT_LOADED; + } else if (trackingMode == TrackingMode.TRACKING_CONTENT_BLOCKED) { + return Mode.TRACKING_CONTENT_BLOCKED; + } else if (activeMixedMode == MixedMode.LOADED) { + return Mode.MIXED_MODE; + } else if (displayMixedMode == MixedMode.LOADED) { + return Mode.WARNING; + } + + return securityModeMap.containsKey(securityMode) + ? securityModeMap.get(securityMode) + : Mode.UNKNOWN; + } + + // Security mode constants, which map to the icons / levels defined in: + // http://dxr.mozilla.org/mozilla-central/source/mobile/android/base/java/org/mozilla/gecko/resources/drawable/customtabs_site_security_level.xml + public enum Mode { + UNKNOWN, + LOCK_SECURE, + WARNING, + MIXED_MODE, + TRACKING_CONTENT_BLOCKED, + TRACKING_CONTENT_LOADED + } +}
--- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -766,16 +766,17 @@ gbjar.sources += ['java/org/mozilla/geck 'toolbar/BrowserToolbarPhoneBase.java', 'toolbar/BrowserToolbarTablet.java', 'toolbar/BrowserToolbarTabletBase.java', 'toolbar/CanvasDelegate.java', 'toolbar/ForwardButton.java', 'toolbar/NavButton.java', 'toolbar/PageActionLayout.java', 'toolbar/PhoneTabsButton.java', + 'toolbar/SecurityModeUtil.java', 'toolbar/ShapedButton.java', 'toolbar/ShapedButtonFrameLayout.java', 'toolbar/SiteIdentityPopup.java', 'toolbar/TabCounter.java', 'toolbar/ToolbarDisplayLayout.java', 'toolbar/ToolbarEditLayout.java', 'toolbar/ToolbarEditText.java', 'toolbar/ToolbarPrefs.java',
new file mode 100644 --- /dev/null +++ b/mobile/android/base/resources/drawable/customtabs_site_security_icon.xml @@ -0,0 +1,27 @@ +<?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/. --> + +<level-list xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:drawable="@drawable/site_security_unknown" + android:maxLevel="0"/> + <item + android:drawable="@drawable/lock_secure" + android:maxLevel="1"/> + <item + android:drawable="@drawable/warning_minor" + android:maxLevel="2"/> + <item + android:drawable="@drawable/lock_disabled" + android:maxLevel="3"/> + <item + android:drawable="@drawable/shield_enabled" + android:maxLevel="4"/> + <item + android:drawable="@drawable/shield_disabled" + android:maxLevel="5"/> + +</level-list>
new file mode 100644 --- /dev/null +++ b/mobile/android/base/resources/layout/customtabs_action_bar_custom_view.xml @@ -0,0 +1,59 @@ +<?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/. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal"> + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingEnd="5dp" + android:paddingRight="5dp"> + + <ImageButton + android:id="@+id/custom_tabs_action_bar_icon" + style="@style/UrlBar.ImageButton" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_gravity="center_vertical" + android:contentDescription="@string/site_security" + android:padding="3dp" + android:scaleType="fitCenter" + android:src="@drawable/customtabs_site_security_icon" + android:visibility="invisible"/> + + </FrameLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:animateLayoutChanges="true" + android:gravity="center_vertical" + android:orientation="vertical"> + + <TextView + android:id="@+id/custom_tabs_action_bar_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:maxLines="1" + android:textColor="?android:textColorPrimary" + android:textSize="14sp" + tools:text="Mozilla.org"/> + + <TextView + android:id="@+id/custom_tabs_action_bar_url" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:maxLines="1" + android:textColor="?android:textColorPrimary" + android:textSize="10sp" + tools:text="https://mozilla.org"/> + + </LinearLayout> + +</LinearLayout>