Bug 768667 - Provide an action mode for compatibility with old Android versions. r=sriram
101
mobile/android/base/ActionModeCompat.java
Normal file
@ -0,0 +1,101 @@
|
||||
/* 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;
|
||||
|
||||
import org.mozilla.gecko.widget.GeckoPopupMenu;
|
||||
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
class ActionModeCompat implements GeckoPopupMenu.OnMenuItemClickListener,
|
||||
View.OnClickListener {
|
||||
private final String LOGTAG = "GeckoActionModeCompat";
|
||||
|
||||
private Callback mCallback;
|
||||
private ActionModeCompatView mView;
|
||||
private Presenter mPresenter;
|
||||
|
||||
/* A set of callbacks to be called during this ActionMode's lifecycle. These will control the
|
||||
* creation, interaction with, and destruction of menuitems for the view */
|
||||
public static interface Callback {
|
||||
/* Called when action mode is first created. Implementors should use this to inflate menu resources. */
|
||||
public boolean onCreateActionMode(ActionModeCompat mode, Menu menu);
|
||||
|
||||
/* Called to refresh an action mode's action menu. Called whenever the mode is invalidated. Implementors
|
||||
* should use this to enable/disable/show/hide menu items. */
|
||||
public boolean onPrepareActionMode(ActionModeCompat mode, Menu menu);
|
||||
|
||||
/* Called to report a user click on an action button. */
|
||||
public boolean onActionItemClicked(ActionModeCompat mode, MenuItem item);
|
||||
|
||||
/* Called when an action mode is about to be exited and destroyed. */
|
||||
public void onDestroyActionMode(ActionModeCompat mode);
|
||||
}
|
||||
|
||||
/* Presenters handle the actual showing/hiding of the action mode UI in the app. Its their responsibility
|
||||
* to create an action mode, and assign it Callbacks and ActionModeCompatView's. */
|
||||
public static interface Presenter {
|
||||
/* Called when an action mode should be shown */
|
||||
public void startActionModeCompat(final Callback callback);
|
||||
|
||||
/* Called when whatever action mode is showing should be hidden */
|
||||
public void endActionModeCompat();
|
||||
}
|
||||
|
||||
public ActionModeCompat(Presenter presenter, Callback callback, ActionModeCompatView view) {
|
||||
mPresenter = presenter;
|
||||
mCallback = callback;
|
||||
|
||||
mView = view;
|
||||
mView.initForMode(this);
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
// Clearing the menu will also clear the ActionItemBar
|
||||
mView.getMenu().clear();
|
||||
if (mCallback != null) {
|
||||
mCallback.onDestroyActionMode(this);
|
||||
}
|
||||
}
|
||||
|
||||
public CharSequence getTitle() {
|
||||
return mView.getTitle();
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
mView.setTitle(title);
|
||||
}
|
||||
|
||||
public void setTitle(int resId) {
|
||||
mView.setTitle(resId);
|
||||
}
|
||||
|
||||
public Menu getMenu() {
|
||||
return mView.getMenu();
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
if (mCallback != null) {
|
||||
mCallback.onPrepareActionMode(this, mView.getMenu());
|
||||
}
|
||||
mView.invalidate();
|
||||
}
|
||||
|
||||
/* GeckoPopupMenu.OnMenuItemClickListener */
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
if (mCallback != null) {
|
||||
return mCallback.onActionItemClicked(this, item);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* View.OnClickListener*/
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mPresenter.endActionModeCompat();
|
||||
}
|
||||
}
|
162
mobile/android/base/ActionModeCompatView.java
Normal file
@ -0,0 +1,162 @@
|
||||
/* 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;
|
||||
|
||||
import org.mozilla.gecko.menu.GeckoMenu;
|
||||
import org.mozilla.gecko.menu.MenuPopup;
|
||||
import org.mozilla.gecko.menu.MenuPanel;
|
||||
import org.mozilla.gecko.menu.MenuItemActionBar;
|
||||
import org.mozilla.gecko.widget.GeckoPopupMenu;
|
||||
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.content.Context;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.Button;
|
||||
import android.widget.PopupWindow;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
class ActionModeCompatView extends LinearLayout implements GeckoMenu.ActionItemBarPresenter {
|
||||
private final String LOGTAG = "GeckoActionModeCompatPresenter";
|
||||
|
||||
private static final int SPEC = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
|
||||
|
||||
private Button mTitleView;
|
||||
private ImageButton mMenuButton;
|
||||
private ViewGroup mActionButtonBar;
|
||||
private GeckoPopupMenu mPopupMenu;
|
||||
|
||||
// Maximum number of items to show as actions
|
||||
private static final int MAX_ACTION_ITEMS = 4;
|
||||
|
||||
private int mActionButtonsWidth = 0;
|
||||
|
||||
public ActionModeCompatView(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public ActionModeCompatView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public ActionModeCompatView(Context context, AttributeSet attrs, int style) {
|
||||
super(context, attrs, style);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public void init(Context context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.actionbar, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
mTitleView = (Button) findViewById(R.id.actionmode_title);
|
||||
mMenuButton = (ImageButton) findViewById(R.id.actionbar_menu);
|
||||
mActionButtonBar = (ViewGroup) findViewById(R.id.actionbar_buttons);
|
||||
|
||||
mPopupMenu = new GeckoPopupMenu(getContext(), mMenuButton);
|
||||
((GeckoMenu) mPopupMenu.getMenu()).setActionItemBarPresenter(this);
|
||||
|
||||
mMenuButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
openMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void initForMode(final ActionModeCompat mode) {
|
||||
mTitleView.setOnClickListener(mode);
|
||||
mPopupMenu.setOnMenuItemClickListener(mode);
|
||||
}
|
||||
|
||||
public CharSequence getTitle() {
|
||||
return mTitleView.getText();
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
mTitleView.setText(title);
|
||||
}
|
||||
|
||||
public void setTitle(int resId) {
|
||||
mTitleView.setText(resId);
|
||||
}
|
||||
|
||||
public Menu getMenu() {
|
||||
return mPopupMenu.getMenu();
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
// onFinishInflate may not have been called yet on some versions of Android
|
||||
if (mPopupMenu != null && mMenuButton != null) {
|
||||
mMenuButton.setVisibility(mPopupMenu.getMenu().hasVisibleItems() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
super.invalidate();
|
||||
}
|
||||
|
||||
/* GeckoMenu.ActionItemBarPresenter */
|
||||
@Override
|
||||
public boolean addActionItem(View actionItem) {
|
||||
final int count = mActionButtonBar.getChildCount();
|
||||
if (count >= MAX_ACTION_ITEMS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int maxWidth = mActionButtonBar.getMeasuredWidth();
|
||||
if (maxWidth == 0) {
|
||||
mActionButtonBar.measure(SPEC, SPEC);
|
||||
maxWidth = mActionButtonBar.getMeasuredWidth();
|
||||
}
|
||||
|
||||
// Since we don't know how many items will be added, we always reserve space for the overflow menu
|
||||
mMenuButton.measure(SPEC, SPEC);
|
||||
maxWidth -= mMenuButton.getMeasuredWidth();
|
||||
|
||||
if (mActionButtonsWidth <= 0) {
|
||||
mActionButtonsWidth = 0;
|
||||
|
||||
// Loop over child views, measure them, and add their width to the taken width
|
||||
for (int i = 0; i < count; i++) {
|
||||
View v = mActionButtonBar.getChildAt(i);
|
||||
v.measure(SPEC, SPEC);
|
||||
mActionButtonsWidth += v.getMeasuredWidth();
|
||||
}
|
||||
}
|
||||
|
||||
actionItem.measure(SPEC, SPEC);
|
||||
int w = actionItem.getMeasuredWidth();
|
||||
if (mActionButtonsWidth + w < maxWidth) {
|
||||
// We cache the new width of our children.
|
||||
mActionButtonsWidth += w;
|
||||
mActionButtonBar.addView(actionItem);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* GeckoMenu.ActionItemBarPresenter */
|
||||
@Override
|
||||
public void removeActionItem(View actionItem) {
|
||||
mActionButtonsWidth -= actionItem.getMeasuredWidth();
|
||||
mActionButtonBar.removeView(actionItem);
|
||||
}
|
||||
|
||||
public void openMenu() {
|
||||
mPopupMenu.openMenu();
|
||||
}
|
||||
|
||||
public void closeMenu() {
|
||||
mPopupMenu.dismiss();
|
||||
}
|
||||
|
||||
}
|
@ -15,6 +15,7 @@ import org.mozilla.gecko.favicons.LoadFaviconTask;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.gfx.GeckoLayerClient;
|
||||
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
|
||||
import org.mozilla.gecko.gfx.LayerMarginsAnimator;
|
||||
import org.mozilla.gecko.health.BrowserHealthRecorder;
|
||||
import org.mozilla.gecko.health.BrowserHealthReporter;
|
||||
import org.mozilla.gecko.home.BrowserSearch;
|
||||
@ -110,10 +111,13 @@ abstract public class BrowserApp extends GeckoApp
|
||||
private BrowserSearch mBrowserSearch;
|
||||
private View mBrowserSearchContainer;
|
||||
|
||||
public ViewFlipper mViewFlipper;
|
||||
public ActionModeCompatView mActionBar;
|
||||
private BrowserToolbar mBrowserToolbar;
|
||||
private HomePager mHomePager;
|
||||
private View mHomePagerContainer;
|
||||
protected Telemetry.Timer mAboutHomeStartupTimer = null;
|
||||
private ActionModeCompat mActionMode;
|
||||
|
||||
private static final int GECKO_TOOLS_MENU = -1;
|
||||
private static final int ADDON_MENU_OFFSET = 1000;
|
||||
@ -264,7 +268,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||
// Toggle/focus the address bar on gamepad-y button.
|
||||
if (mBrowserToolbar.isVisible()) {
|
||||
if (mViewFlipper.getVisibility() == View.VISIBLE) {
|
||||
if (isDynamicToolbarEnabled() && !isHomePagerVisible()) {
|
||||
if (mLayerView != null) {
|
||||
mLayerView.getLayerMarginsAnimator().hideMargins(false);
|
||||
@ -424,6 +428,9 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mViewFlipper = (ViewFlipper) findViewById(R.id.browser_actionbar);
|
||||
mActionBar = (ActionModeCompatView) findViewById(R.id.actionbar);
|
||||
|
||||
mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar);
|
||||
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
|
||||
// Show the target URL immediately in the toolbar
|
||||
@ -601,6 +608,11 @@ abstract public class BrowserApp extends GeckoApp
|
||||
return;
|
||||
}
|
||||
|
||||
if (mActionMode != null) {
|
||||
endActionModeCompat();
|
||||
return;
|
||||
}
|
||||
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@ -961,8 +973,8 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
public void refreshToolbarHeight() {
|
||||
int height = 0;
|
||||
if (mBrowserToolbar != null) {
|
||||
height = mBrowserToolbar.getHeight();
|
||||
if (mViewFlipper != null) {
|
||||
height = mViewFlipper.getHeight();
|
||||
}
|
||||
|
||||
if (!isDynamicToolbarEnabled() || isHomePagerVisible()) {
|
||||
@ -995,9 +1007,9 @@ abstract public class BrowserApp extends GeckoApp
|
||||
@Override
|
||||
public void run() {
|
||||
if (aShow) {
|
||||
mBrowserToolbar.show();
|
||||
mViewFlipper.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mBrowserToolbar.hide();
|
||||
mViewFlipper.setVisibility(View.GONE);
|
||||
if (hasTabsSideBar()) {
|
||||
hideTabs();
|
||||
}
|
||||
@ -1013,8 +1025,8 @@ abstract public class BrowserApp extends GeckoApp
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mBrowserToolbar.show();
|
||||
mBrowserToolbar.requestFocusFromTouch();
|
||||
mViewFlipper.setVisibility(View.VISIBLE);
|
||||
mViewFlipper.requestFocusFromTouch();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1977,13 +1989,13 @@ abstract public class BrowserApp extends GeckoApp
|
||||
@Override
|
||||
public void run() {
|
||||
if (fullscreen) {
|
||||
mBrowserToolbar.hide();
|
||||
mViewFlipper.setVisibility(View.GONE);
|
||||
if (isDynamicToolbarEnabled()) {
|
||||
mLayerView.getLayerMarginsAnimator().hideMargins(true);
|
||||
mLayerView.getLayerMarginsAnimator().setMaxMargins(0, 0, 0, 0);
|
||||
}
|
||||
} else {
|
||||
mBrowserToolbar.show();
|
||||
mViewFlipper.setVisibility(View.VISIBLE);
|
||||
if (isDynamicToolbarEnabled()) {
|
||||
mLayerView.getLayerMarginsAnimator().showMargins(true);
|
||||
mLayerView.getLayerMarginsAnimator().setMaxMargins(0, mToolbarHeight, 0, 0);
|
||||
@ -2465,4 +2477,38 @@ abstract public class BrowserApp extends GeckoApp
|
||||
Log.w(LOGTAG, "No candidate updater found; ignoring launch request.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Implementing ActionModeCompat.Presenter */
|
||||
@Override
|
||||
public void startActionModeCompat(final ActionModeCompat.Callback callback) {
|
||||
// If actionMode is null, we're not currently showing one. Flip to the action mode view
|
||||
if (mActionMode == null) {
|
||||
mViewFlipper.showNext();
|
||||
LayerMarginsAnimator margins = mLayerView.getLayerMarginsAnimator();
|
||||
margins.setMaxMargins(0, mViewFlipper.getHeight(), 0, 0);
|
||||
margins.setMarginsPinned(true);
|
||||
margins.showMargins(false);
|
||||
} else {
|
||||
// Otherwise, we're already showing an action mode. Just finish it and show the new one
|
||||
mActionMode.finish();
|
||||
}
|
||||
|
||||
mActionMode = new ActionModeCompat(BrowserApp.this, callback, mActionBar);
|
||||
if (callback.onCreateActionMode(mActionMode, mActionMode.getMenu())) {
|
||||
mActionMode.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementing ActionModeCompat.Presenter */
|
||||
@Override
|
||||
public void endActionModeCompat() {
|
||||
if (mActionMode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mActionMode.finish();
|
||||
mActionMode = null;
|
||||
mLayerView.getLayerMarginsAnimator().setMarginsPinned(false);
|
||||
mViewFlipper.showPrevious();
|
||||
}
|
||||
}
|
||||
|
@ -372,3 +372,8 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
|
||||
<!ENTITY exit_guest_session_title "&brandShortName; will now restart">
|
||||
<!ENTITY exit_guest_session_text "The browsing data from this session will be deleted.">
|
||||
|
||||
<!-- These are only used for accessiblity for the done and overflow-menu buttons in the actionbar.
|
||||
They are never shown to users -->
|
||||
<!ENTITY actionbar_menu "Menu">
|
||||
<!ENTITY actionbar_done "Done">
|
||||
|
@ -66,7 +66,7 @@ public class GeckoMenu extends ListView
|
||||
*/
|
||||
public static interface ActionItemBarPresenter {
|
||||
// Add an action-item.
|
||||
public void addActionItem(View actionItem);
|
||||
public boolean addActionItem(View actionItem);
|
||||
|
||||
// Remove an action-item.
|
||||
public void removeActionItem(View actionItem);
|
||||
@ -151,7 +151,7 @@ public class GeckoMenu extends ListView
|
||||
mItems.add(menuItem);
|
||||
}
|
||||
|
||||
private void addActionItem(final GeckoMenuItem menuItem) {
|
||||
private boolean addActionItem(final GeckoMenuItem menuItem) {
|
||||
menuItem.setOnShowAsActionChangedListener(this);
|
||||
|
||||
if (mActionItems.size() == 0 &&
|
||||
@ -173,6 +173,8 @@ public class GeckoMenu extends ListView
|
||||
mActionItems.put(menuItem, actionView);
|
||||
mActionItemBarPresenter.addActionItem(actionView);
|
||||
mItems.add(menuItem);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -224,6 +226,12 @@ public class GeckoMenu extends ListView
|
||||
mAdapter.clear();
|
||||
|
||||
mItems.clear();
|
||||
|
||||
if (mActionItemBarPresenter != null) {
|
||||
for (View item : mActionItems.values()) {
|
||||
mActionItemBarPresenter.removeActionItem(item);
|
||||
}
|
||||
}
|
||||
mActionItems.clear();
|
||||
}
|
||||
|
||||
@ -266,7 +274,7 @@ public class GeckoMenu extends ListView
|
||||
@Override
|
||||
public boolean hasVisibleItems() {
|
||||
for (GeckoMenuItem menuItem : mItems) {
|
||||
if (menuItem.isVisible())
|
||||
if (menuItem.isVisible() && !mActionItems.containsKey(menuItem))
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -362,10 +370,11 @@ public class GeckoMenu extends ListView
|
||||
public void onShowAsActionChanged(GeckoMenuItem item, boolean isActionItem) {
|
||||
removeItem(item.getItemId());
|
||||
|
||||
if (isActionItem)
|
||||
addActionItem(item);
|
||||
else
|
||||
addItem(item);
|
||||
if (isActionItem && addActionItem(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
addItem(item);
|
||||
}
|
||||
|
||||
public void onItemChanged(GeckoMenuItem item) {
|
||||
@ -476,11 +485,12 @@ public class GeckoMenu extends ListView
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addActionItem(View actionItem) {
|
||||
public boolean addActionItem(View actionItem) {
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(actionItem.getLayoutParams());
|
||||
params.weight = 1.0f;
|
||||
actionItem.setLayoutParams(params);
|
||||
addView(actionItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,6 +85,8 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
gbjar = add_java_jar('gecko-browser')
|
||||
gbjar.sources += [
|
||||
'AboutPages.java',
|
||||
'ActionModeCompat.java',
|
||||
'ActionModeCompatView.java',
|
||||
'ActivityHandlerHelper.java',
|
||||
'AlertNotification.java',
|
||||
'animation/AnimatorProxy.java',
|
||||
@ -409,6 +411,8 @@ ANDROID_RESFILES += [
|
||||
'resources/drawable-hdpi-v11/ic_menu_share.png',
|
||||
'resources/drawable-hdpi-v11/ic_menu_tools.png',
|
||||
'resources/drawable-hdpi-v11/ic_status_logo.png',
|
||||
'resources/drawable-hdpi/ab_done.png',
|
||||
'resources/drawable-hdpi/ab_stacked_transparent_light_holo.9.png',
|
||||
'resources/drawable-hdpi/abouthome_thumbnail.png',
|
||||
'resources/drawable-hdpi/alert_addon.png',
|
||||
'resources/drawable-hdpi/alert_app.png',
|
||||
@ -466,6 +470,7 @@ ANDROID_RESFILES += [
|
||||
'resources/drawable-hdpi/menu_item_check.png',
|
||||
'resources/drawable-hdpi/menu_item_more.png',
|
||||
'resources/drawable-hdpi/menu_item_uncheck.png',
|
||||
'resources/drawable-hdpi/menu_light.png',
|
||||
'resources/drawable-hdpi/menu_panel_bg.9.png',
|
||||
'resources/drawable-hdpi/menu_pb.png',
|
||||
'resources/drawable-hdpi/menu_popup_arrow_bottom.png',
|
||||
@ -547,6 +552,8 @@ ANDROID_RESFILES += [
|
||||
'resources/drawable-mdpi-v11/ic_menu_share.png',
|
||||
'resources/drawable-mdpi-v11/ic_menu_tools.png',
|
||||
'resources/drawable-mdpi-v11/ic_status_logo.png',
|
||||
'resources/drawable-mdpi/ab_done.png',
|
||||
'resources/drawable-mdpi/ab_stacked_transparent_light_holo.9.png',
|
||||
'resources/drawable-mdpi/abouthome_thumbnail.png',
|
||||
'resources/drawable-mdpi/alert_addon.png',
|
||||
'resources/drawable-mdpi/alert_app.png',
|
||||
@ -607,6 +614,7 @@ ANDROID_RESFILES += [
|
||||
'resources/drawable-mdpi/menu_item_check.png',
|
||||
'resources/drawable-mdpi/menu_item_more.png',
|
||||
'resources/drawable-mdpi/menu_item_uncheck.png',
|
||||
'resources/drawable-mdpi/menu_light.png',
|
||||
'resources/drawable-mdpi/menu_panel_bg.9.png',
|
||||
'resources/drawable-mdpi/menu_pb.png',
|
||||
'resources/drawable-mdpi/menu_popup_arrow_bottom.png',
|
||||
@ -683,6 +691,8 @@ ANDROID_RESFILES += [
|
||||
'resources/drawable-xhdpi-v11/ic_menu_share.png',
|
||||
'resources/drawable-xhdpi-v11/ic_menu_tools.png',
|
||||
'resources/drawable-xhdpi-v11/ic_status_logo.png',
|
||||
'resources/drawable-xhdpi/ab_done.png',
|
||||
'resources/drawable-xhdpi/ab_stacked_transparent_light_holo.9.png',
|
||||
'resources/drawable-xhdpi/abouthome_thumbnail.png',
|
||||
'resources/drawable-xhdpi/alert_addon.png',
|
||||
'resources/drawable-xhdpi/alert_app.png',
|
||||
@ -738,6 +748,7 @@ ANDROID_RESFILES += [
|
||||
'resources/drawable-xhdpi/menu_item_check.png',
|
||||
'resources/drawable-xhdpi/menu_item_more.png',
|
||||
'resources/drawable-xhdpi/menu_item_uncheck.png',
|
||||
'resources/drawable-xhdpi/menu_light.png',
|
||||
'resources/drawable-xhdpi/menu_panel_bg.9.png',
|
||||
'resources/drawable-xhdpi/menu_pb.png',
|
||||
'resources/drawable-xhdpi/menu_popup_arrow_bottom.png',
|
||||
@ -835,6 +846,7 @@ ANDROID_RESFILES += [
|
||||
'resources/layout-xlarge-v11/home_history_tabs_indicator.xml',
|
||||
'resources/layout-xlarge-v11/remote_tabs_child.xml',
|
||||
'resources/layout-xlarge-v11/remote_tabs_group.xml',
|
||||
'resources/layout/actionbar.xml',
|
||||
'resources/layout/arrow_popup.xml',
|
||||
'resources/layout/autocomplete_list.xml',
|
||||
'resources/layout/autocomplete_list_item.xml',
|
||||
|
BIN
mobile/android/base/resources/drawable-hdpi/ab_done.png
Normal file
After Width: | Height: | Size: 737 B |
After Width: | Height: | Size: 222 B |
BIN
mobile/android/base/resources/drawable-hdpi/menu_light.png
Normal file
After Width: | Height: | Size: 148 B |
BIN
mobile/android/base/resources/drawable-mdpi/ab_done.png
Normal file
After Width: | Height: | Size: 552 B |
After Width: | Height: | Size: 210 B |
BIN
mobile/android/base/resources/drawable-mdpi/menu_light.png
Normal file
After Width: | Height: | Size: 131 B |
BIN
mobile/android/base/resources/drawable-xhdpi/ab_done.png
Normal file
After Width: | Height: | Size: 915 B |
After Width: | Height: | Size: 242 B |
BIN
mobile/android/base/resources/drawable-xhdpi/menu_light.png
Normal file
After Width: | Height: | Size: 184 B |
33
mobile/android/base/resources/layout/actionbar.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?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/. -->
|
||||
|
||||
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<Button android:id="@+id/actionmode_title"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="wrap_content"
|
||||
style="@style/GeckoActionBar.Title"/>
|
||||
|
||||
<!-- Draw a separator to the left of the title -->
|
||||
<View android:layout_height="fill_parent"
|
||||
android:layout_width="1dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@android:color/darker_gray"/>
|
||||
|
||||
<LinearLayout android:id="@+id/actionbar_buttons"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1"
|
||||
style="@style/GeckoActionBar.Buttons"/>
|
||||
|
||||
<ImageButton android:id="@+id/actionbar_menu"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="@dimen/browser_toolbar_icon_width"
|
||||
style="@style/GeckoActionBar.Button.MenuButton"/>
|
||||
|
||||
</merge>
|
@ -67,15 +67,29 @@
|
||||
view. To make sure the EditText is not the first focusable view in
|
||||
the root view, BrowserToolbar should be specified as low in the
|
||||
view hierarchy as possible. -->
|
||||
<org.mozilla.gecko.toolbar.BrowserToolbar
|
||||
android:id="@id/browser_toolbar"
|
||||
style="@style/BrowserToolbar"
|
||||
|
||||
<ViewFlipper android:id="@+id/browser_actionbar"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/browser_toolbar_height"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<org.mozilla.gecko.toolbar.BrowserToolbar
|
||||
android:id="@id/browser_toolbar"
|
||||
style="@style/BrowserToolbar"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/browser_toolbar_height"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/url_bar_bg"/>
|
||||
|
||||
<org.mozilla.gecko.ActionModeCompatView android:id="@+id/actionbar"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="fill_parent"
|
||||
style="@style/GeckoActionBar"/>
|
||||
|
||||
</ViewFlipper>
|
||||
|
||||
</view>
|
||||
|
||||
<LinearLayout android:id="@+id/toast"
|
||||
|
@ -74,4 +74,20 @@
|
||||
<item name="android:textColor">#FF000000</item>
|
||||
</style>
|
||||
|
||||
<style name="GeckoActionBar" parent="@android:style/Widget.Holo.Light.ActionMode">
|
||||
<item name="android:background">@drawable/ab_stacked_transparent_light_holo</item>
|
||||
</style>
|
||||
|
||||
<style name="GeckoActionBar.Title" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
|
||||
<item name="android:drawableLeft">?android:attr/actionModeCloseDrawable</item>
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:paddingLeft">15dp</item>
|
||||
<item name="android:paddingRight">15dp</item>
|
||||
</style>
|
||||
|
||||
<style name="GeckoActionBar.Button.MenuButton" parent="android:style/Widget.Holo.Light.ActionButton.Overflow">
|
||||
<item name="android:scaleType">center</item>
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -540,4 +540,37 @@
|
||||
<item name="android:layout_gravity">center_vertical</item>
|
||||
</style>
|
||||
|
||||
<style name="GeckoActionBar">
|
||||
<item name="android:background">@drawable/ab_stacked_transparent_light_holo</item>
|
||||
</style>
|
||||
|
||||
<style name="GeckoActionBar.Title">
|
||||
<item name="android:gravity">center_vertical</item>
|
||||
<item name="android:minWidth">0dp</item>
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:textAppearance">@style/TextAppearance.Medium</item>
|
||||
<item name="android:drawableLeft">@drawable/ab_done</item>
|
||||
<item name="android:paddingLeft">15dp</item>
|
||||
<item name="android:paddingRight">15dp</item>
|
||||
<item name="android:contentDescription">@string/actionbar_done</item>
|
||||
</style>
|
||||
|
||||
<style name="GeckoActionBar.Button" parent="Widget.MenuItemActionBar">
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:gravity">center</item>
|
||||
</style>
|
||||
|
||||
<style name="GeckoActionBar.Button.MenuButton">
|
||||
<item name="android:scaleType">center</item>
|
||||
<item name="android:src">@drawable/menu_light</item>
|
||||
<item name="android:contentDescription">@string/actionbar_menu</item>
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="GeckoActionBar.Buttons">
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:textColor">@color/text_color_primary</item>
|
||||
<item name="android:gravity">right</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -82,6 +82,8 @@
|
||||
<item name="topSitesGridViewStyle">@style/Widget.TopSitesGridView</item>
|
||||
<item name="topSitesThumbnailViewStyle">@style/Widget.TopSitesThumbnailView</item>
|
||||
<item name="homeListViewStyle">@style/Widget.HomeListView</item>
|
||||
<item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
|
||||
<item name="menuItemActionBarStyle">@style/Widget.MenuItemActionBar</item>
|
||||
</style>
|
||||
|
||||
<style name="Gecko.Preferences" parent="GeckoPreferencesBase"/>
|
||||
|
@ -350,4 +350,8 @@
|
||||
|
||||
<string name="exit_guest_session_title">&exit_guest_session_title;</string>
|
||||
<string name="exit_guest_session_text">&exit_guest_session_text;</string>
|
||||
|
||||
<string name="actionbar_menu">&actionbar_menu;</string>
|
||||
<string name="actionbar_done">&actionbar_done;</string>
|
||||
|
||||
</resources>
|
||||
|
@ -22,6 +22,7 @@ import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
@ -835,4 +836,51 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
t.printStackTrace(new PrintWriter(sw));
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition class that waits for a view, and allows callers access it when done.
|
||||
*/
|
||||
private class DescriptionCondition<T extends View> implements Condition {
|
||||
public T mView;
|
||||
private String mDescr;
|
||||
private Class<T> mCls;
|
||||
|
||||
public DescriptionCondition(Class<T> cls, String descr) {
|
||||
mDescr = descr;
|
||||
mCls = cls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfied() {
|
||||
mView = findViewWithContentDescription(mCls, mDescr);
|
||||
return mView != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a view with the specified description .
|
||||
*/
|
||||
public <T extends View> T waitForViewWithDescription(Class<T> cls, String description) {
|
||||
DescriptionCondition<T> c = new DescriptionCondition<T>(cls, description);
|
||||
waitForCondition(c, MAX_WAIT_ENABLED_TEXT_MS);
|
||||
return c.mView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an active view with the specified description .
|
||||
*/
|
||||
public <T extends View> T findViewWithContentDescription(Class<T> cls, String description) {
|
||||
for (T view : mSolo.getCurrentViews(cls)) {
|
||||
final String descr = (String) view.getContentDescription();
|
||||
if (TextUtils.isEmpty(descr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TextUtils.equals(description, descr)) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import org.mozilla.gecko.*;
|
||||
import android.view.View;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ImageView;
|
||||
import java.util.ArrayList;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@ -68,10 +69,11 @@ public class testAddSearchEngine extends AboutHomeTest {
|
||||
mAsserter.dumpLog("Long Clicking at width = " + String.valueOf(width) + " and height = " + String.valueOf(height));
|
||||
mSolo.clickLongOnScreen(width,height);
|
||||
|
||||
mAsserter.ok(waitForText(ADD_SEARCHENGINE_OPTION_TEXT), "Waiting for the context menu to be opened", "The context menu was opened");
|
||||
ImageView view = waitForViewWithDescription(ImageView.class, ADD_SEARCHENGINE_OPTION_TEXT);
|
||||
mAsserter.isnot(view, null, "The action mode was opened");
|
||||
|
||||
// Add the search engine
|
||||
mSolo.clickOnText(ADD_SEARCHENGINE_OPTION_TEXT);
|
||||
mSolo.clickOnView(view);
|
||||
waitForText("Cancel");
|
||||
clickOnButton("OK");
|
||||
mAsserter.ok(!mSolo.searchText(ADD_SEARCHENGINE_OPTION_TEXT), "Adding the Search Engine", "The add Search Engine pop-up has been closed");
|
||||
|
@ -1455,8 +1455,9 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addActionItem(View actionItem) {
|
||||
public boolean addActionItem(View actionItem) {
|
||||
mActionItemBar.addView(actionItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,6 +65,9 @@ public class GeckoPopupMenu implements GeckoMenu.Callback,
|
||||
mMenuPopup = new MenuPopup(context);
|
||||
mMenuPanel = new MenuPanel(context, null);
|
||||
|
||||
mMenuPanel.addView(mMenu);
|
||||
mMenuPopup.setPanelView(mMenuPanel);
|
||||
|
||||
setAnchor(anchor);
|
||||
}
|
||||
|
||||
@ -95,8 +98,6 @@ public class GeckoPopupMenu implements GeckoMenu.Callback,
|
||||
if (menuRes > 0) {
|
||||
mMenuInflater.inflate(menuRes, mMenu);
|
||||
}
|
||||
mMenuPanel.addView(mMenu);
|
||||
mMenuPopup.setPanelView(mMenuPanel);
|
||||
}
|
||||
|
||||
/**
|
||||
|