The client implementation for remote selection bar
The DefaultSelectionToolbarRenderService renders the menu items and callbacks to the application with the SurfacePackage and the relative postition, the application will set the SurfacePackage to the SurfaceView and put the PopupWindow on the correct position. The MenuItemRepr is a copy from LocalFloatingToolbarPopup, no logic change in this class. The above changes don't contain the solution for transfering the touch focus on the non-toolbar part to the application process. The part will be implemented on the follow up change. Bug: 190030331 Bug: 205823020 Test: manual. Enable the flag, try to selectin the text then the toolbar can show successfully. Can open opverflow menu, scroll the view, the selection toolbar also changes the position. Merged-In: I3915e8165b4d371700cb5fb9e7eb23d46f39bd5c Change-Id: I1f2add3e93cee9d7af20cba9ccd4e0b393afffcb
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.internal.widget.floatingtoolbar;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.view.MenuItem;
|
||||
@@ -84,7 +85,8 @@ public interface FloatingToolbarPopup {
|
||||
* @see PopupWindow#setFocusable(boolean)
|
||||
* @see PopupWindow.OnDismissListener
|
||||
*/
|
||||
boolean setOutsideTouchable(boolean outsideTouchable, PopupWindow.OnDismissListener onDismiss);
|
||||
boolean setOutsideTouchable(boolean outsideTouchable,
|
||||
@Nullable PopupWindow.OnDismissListener onDismiss);
|
||||
|
||||
/**
|
||||
* Returns {@link RemoteFloatingToolbarPopup} implementation if the system selection toolbar
|
||||
|
||||
@@ -16,13 +16,47 @@
|
||||
|
||||
package com.android.internal.widget.floatingtoolbar;
|
||||
|
||||
import static android.view.selectiontoolbar.SelectionToolbarManager.NO_TOOLBAR_ID;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UiThread;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.selectiontoolbar.ISelectionToolbarCallback;
|
||||
import android.view.selectiontoolbar.SelectionToolbarManager;
|
||||
import android.view.selectiontoolbar.ShowInfo;
|
||||
import android.view.selectiontoolbar.ToolbarMenuItem;
|
||||
import android.view.selectiontoolbar.WidgetInfo;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -34,55 +68,495 @@ import java.util.Objects;
|
||||
*/
|
||||
public final class RemoteFloatingToolbarPopup implements FloatingToolbarPopup {
|
||||
|
||||
private final SelectionToolbarManager mSelectionToolbarManager;
|
||||
// Parent for the popup window.
|
||||
private final View mParent;
|
||||
private static final boolean DEBUG =
|
||||
Log.isLoggable(FloatingToolbar.FLOATING_TOOLBAR_TAG, Log.VERBOSE);
|
||||
|
||||
public RemoteFloatingToolbarPopup(Context context, View parent) {
|
||||
// TODO: implement it
|
||||
mParent = Objects.requireNonNull(parent);
|
||||
mSelectionToolbarManager = context.getSystemService(SelectionToolbarManager.class);
|
||||
private static final int TOOLBAR_STATE_SHOWN = 1;
|
||||
private static final int TOOLBAR_STATE_HIDDEN = 2;
|
||||
private static final int TOOLBAR_STATE_DISMISSED = 3;
|
||||
|
||||
@IntDef(prefix = {"TOOLBAR_STATE_"}, value = {
|
||||
TOOLBAR_STATE_SHOWN,
|
||||
TOOLBAR_STATE_HIDDEN,
|
||||
TOOLBAR_STATE_DISMISSED
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ToolbarState {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private final SelectionToolbarManager mSelectionToolbarManager;
|
||||
// Parent for the popup window.
|
||||
@NonNull
|
||||
private final View mParent;
|
||||
// A popup window used for showing menu items rendered by the remote system process
|
||||
@NonNull
|
||||
private final PopupWindow mPopupWindow;
|
||||
// The callback to handle remote rendered selection toolbar.
|
||||
@NonNull
|
||||
private final SelectionToolbarCallbackImpl mSelectionToolbarCallback;
|
||||
|
||||
// tracks this popup state.
|
||||
private @ToolbarState int mState;
|
||||
|
||||
// The token of the current showing floating toolbar.
|
||||
private long mFloatingToolbarToken;
|
||||
private final Rect mPreviousContentRect = new Rect();
|
||||
private List<MenuItem> mMenuItems;
|
||||
private MenuItem.OnMenuItemClickListener mMenuItemClickListener;
|
||||
private int mSuggestedWidth;
|
||||
private final Rect mScreenViewPort = new Rect();
|
||||
private boolean mWidthChanged = true;
|
||||
|
||||
private final int[] mCoordsOnScreen = new int[2];
|
||||
private final int[] mCoordsOnWindow = new int[2];
|
||||
|
||||
public RemoteFloatingToolbarPopup(Context context, View parent) {
|
||||
mParent = Objects.requireNonNull(parent);
|
||||
mPopupWindow = createPopupWindow(context);
|
||||
mSelectionToolbarManager = context.getSystemService(SelectionToolbarManager.class);
|
||||
mSelectionToolbarCallback = new SelectionToolbarCallbackImpl(this);
|
||||
mFloatingToolbarToken = NO_TOOLBAR_ID;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void show(List<MenuItem> menuItems,
|
||||
MenuItem.OnMenuItemClickListener menuItemClickListener, Rect contentRect) {
|
||||
// TODO: implement it
|
||||
Objects.requireNonNull(menuItems);
|
||||
Objects.requireNonNull(menuItemClickListener);
|
||||
if (isShowing() && Objects.equals(menuItems, mMenuItems)
|
||||
&& Objects.equals(contentRect, mPreviousContentRect)) {
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"Ignore duplicate show() for the same content.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isLayoutRequired = mMenuItems == null
|
||||
|| !MenuItemRepr.reprEquals(menuItems, mMenuItems)
|
||||
|| mWidthChanged;
|
||||
if (isLayoutRequired) {
|
||||
mSelectionToolbarManager.dismissToolbar(mFloatingToolbarToken);
|
||||
doDismissPopupWindow();
|
||||
}
|
||||
mMenuItemClickListener = menuItemClickListener;
|
||||
mMenuItems = menuItems;
|
||||
|
||||
mParent.getWindowVisibleDisplayFrame(mScreenViewPort);
|
||||
final int suggestWidth = mSuggestedWidth > 0
|
||||
? mSuggestedWidth
|
||||
: mParent.getResources().getDimensionPixelSize(
|
||||
R.dimen.floating_toolbar_preferred_width);
|
||||
final ShowInfo showInfo = new ShowInfo(
|
||||
mFloatingToolbarToken, isLayoutRequired,
|
||||
getToolbarMenuItems(mMenuItems),
|
||||
contentRect,
|
||||
suggestWidth,
|
||||
mScreenViewPort,
|
||||
mParent.getViewRootImpl().getInputToken());
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"RemoteFloatingToolbarPopup.show() for " + showInfo);
|
||||
}
|
||||
mSelectionToolbarManager.showToolbar(showInfo, mSelectionToolbarCallback);
|
||||
mPreviousContentRect.set(contentRect);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void dismiss() {
|
||||
if (mState == TOOLBAR_STATE_DISMISSED) {
|
||||
Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"The floating toolbar already dismissed.");
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"RemoteFloatingToolbarPopup.dismiss().");
|
||||
}
|
||||
mSelectionToolbarManager.dismissToolbar(mFloatingToolbarToken);
|
||||
doDismissPopupWindow();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void hide() {
|
||||
// TODO: implement it
|
||||
if (mState == TOOLBAR_STATE_DISMISSED || mState == TOOLBAR_STATE_HIDDEN) {
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"The floating toolbar already dismissed or hidden.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"RemoteFloatingToolbarPopup.hide().");
|
||||
}
|
||||
mSelectionToolbarManager.hideToolbar(mFloatingToolbarToken);
|
||||
mState = TOOLBAR_STATE_HIDDEN;
|
||||
mPopupWindow.dismiss();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void setSuggestedWidth(int suggestedWidth) {
|
||||
// TODO: implement it
|
||||
int difference = Math.abs(suggestedWidth - mSuggestedWidth);
|
||||
mWidthChanged = difference > (mSuggestedWidth * 0.2);
|
||||
mSuggestedWidth = suggestedWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWidthChanged(boolean widthChanged) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
// TODO: implement it
|
||||
mWidthChanged = widthChanged;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return false;
|
||||
return mState == TOOLBAR_STATE_HIDDEN;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public boolean isShowing() {
|
||||
return false;
|
||||
return mState == TOOLBAR_STATE_SHOWN;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public boolean setOutsideTouchable(boolean outsideTouchable,
|
||||
PopupWindow.OnDismissListener onDismiss) {
|
||||
return false;
|
||||
@Nullable PopupWindow.OnDismissListener onDismiss) {
|
||||
if (mState == TOOLBAR_STATE_DISMISSED) {
|
||||
return false;
|
||||
}
|
||||
boolean ret = false;
|
||||
if (mPopupWindow.isOutsideTouchable() ^ outsideTouchable) {
|
||||
mPopupWindow.setOutsideTouchable(outsideTouchable);
|
||||
mPopupWindow.setFocusable(!outsideTouchable);
|
||||
mPopupWindow.update();
|
||||
ret = true;
|
||||
}
|
||||
mPopupWindow.setOnDismissListener(onDismiss);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void updatePopupWindowContent(WidgetInfo widgetInfo) {
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG, "updatePopupWindowContent.");
|
||||
}
|
||||
ViewGroup contentContainer = (ViewGroup) mPopupWindow.getContentView();
|
||||
contentContainer.removeAllViews();
|
||||
SurfaceView surfaceView = new SurfaceView(mParent.getContext());
|
||||
surfaceView.setZOrderOnTop(true);
|
||||
surfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
|
||||
surfaceView.setChildSurfacePackage(widgetInfo.getSurfacePackage());
|
||||
contentContainer.addView(surfaceView);
|
||||
}
|
||||
|
||||
private MenuItem getMenuItemByToolbarMenuItem(ToolbarMenuItem toolbarMenuItem) {
|
||||
for (MenuItem item : mMenuItems) {
|
||||
if (toolbarMenuItem.getItemId() == item.getItemId()) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Point getCoordinatesInWindow(int x, int y) {
|
||||
// We later specify the location of PopupWindow relative to the attached window.
|
||||
// The idea here is that 1) we can get the location of a View in both window coordinates
|
||||
// and screen coordinates, where the offset between them should be equal to the window
|
||||
// origin, and 2) we can use an arbitrary for this calculation while calculating the
|
||||
// location of the rootview is supposed to be least expensive.
|
||||
// TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can avoid
|
||||
// the following calculation.
|
||||
mParent.getRootView().getLocationOnScreen(mCoordsOnScreen);
|
||||
mParent.getRootView().getLocationInWindow(mCoordsOnWindow);
|
||||
int windowLeftOnScreen = mCoordsOnScreen[0] - mCoordsOnWindow[0];
|
||||
int windowTopOnScreen = mCoordsOnScreen[1] - mCoordsOnWindow[1];
|
||||
return new Point(Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
|
||||
}
|
||||
|
||||
private static List<ToolbarMenuItem> getToolbarMenuItems(List<MenuItem> menuItems) {
|
||||
final List<ToolbarMenuItem> list = new ArrayList<>(menuItems.size());
|
||||
for (MenuItem menuItem : menuItems) {
|
||||
// TODO: use ToolbarMenuItem.Builder(MenuItem) instead
|
||||
ToolbarMenuItem toolbarMenuItem = new ToolbarMenuItem.Builder(menuItem.getItemId(),
|
||||
menuItem.getTitle(), menuItem.getContentDescription(), menuItem.getGroupId(),
|
||||
convertDrawableToIcon(menuItem.getIcon()),
|
||||
menuItem.getTooltipText(),
|
||||
ToolbarMenuItem.getPriorityFromMenuItem(menuItem)).build();
|
||||
list.add(toolbarMenuItem);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static Icon convertDrawableToIcon(Drawable drawable) {
|
||||
if (drawable == null) {
|
||||
return null;
|
||||
}
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
final BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
|
||||
if (bitmapDrawable.getBitmap() != null) {
|
||||
return Icon.createWithBitmap(bitmapDrawable.getBitmap());
|
||||
}
|
||||
}
|
||||
final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
|
||||
drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
final Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
return Icon.createWithBitmap(bitmap);
|
||||
}
|
||||
|
||||
private static PopupWindow createPopupWindow(Context content) {
|
||||
ViewGroup popupContentHolder = new LinearLayout(content);
|
||||
PopupWindow popupWindow = new PopupWindow(popupContentHolder);
|
||||
popupWindow.setClippingEnabled(false);
|
||||
popupWindow.setWindowLayoutType(
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
|
||||
popupWindow.setAnimationStyle(0);
|
||||
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
return popupWindow;
|
||||
}
|
||||
|
||||
private void doDismissPopupWindow() {
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG, "RemoteFloatingToolbarPopup.doDismiss().");
|
||||
}
|
||||
mState = TOOLBAR_STATE_DISMISSED;
|
||||
mMenuItems = null;
|
||||
mMenuItemClickListener = null;
|
||||
mFloatingToolbarToken = 0;
|
||||
mSuggestedWidth = 0;
|
||||
mWidthChanged = true;
|
||||
resetCoords();
|
||||
mPreviousContentRect.setEmpty();
|
||||
mScreenViewPort.setEmpty();
|
||||
mPopupWindow.dismiss();
|
||||
}
|
||||
|
||||
private void resetCoords() {
|
||||
mCoordsOnScreen[0] = 0;
|
||||
mCoordsOnScreen[1] = 0;
|
||||
mCoordsOnWindow[0] = 0;
|
||||
mCoordsOnWindow[1] = 0;
|
||||
}
|
||||
|
||||
private void runOnUiThread(Runnable runnable) {
|
||||
mParent.post(runnable);
|
||||
}
|
||||
|
||||
private void onShow(WidgetInfo info) {
|
||||
runOnUiThread(() -> {
|
||||
mFloatingToolbarToken = info.getWidgetToken();
|
||||
mState = TOOLBAR_STATE_SHOWN;
|
||||
updatePopupWindowContent(info);
|
||||
Rect contentRect = info.getContentRect();
|
||||
mPopupWindow.setWidth(contentRect.width());
|
||||
mPopupWindow.setHeight(contentRect.height());
|
||||
final Point coords = getCoordinatesInWindow(contentRect.left, contentRect.top);
|
||||
mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, coords.x, coords.y);
|
||||
});
|
||||
}
|
||||
|
||||
private void onWidgetUpdated(WidgetInfo info) {
|
||||
runOnUiThread(() -> {
|
||||
if (!isShowing()) {
|
||||
Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"onWidgetUpdated(): The widget isn't showing.");
|
||||
return;
|
||||
}
|
||||
updatePopupWindowContent(info);
|
||||
Rect contentRect = info.getContentRect();
|
||||
Point coords = getCoordinatesInWindow(contentRect.left, contentRect.top);
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"PopupWindow x= " + coords.x + " y= " + coords.y + " w="
|
||||
+ contentRect.width() + " h=" + contentRect.height());
|
||||
}
|
||||
mPopupWindow.update(coords.x, coords.y, contentRect.width(), contentRect.height());
|
||||
});
|
||||
}
|
||||
|
||||
private void onToolbarShowTimeout() {
|
||||
runOnUiThread(() -> {
|
||||
if (mState == TOOLBAR_STATE_DISMISSED) {
|
||||
return;
|
||||
}
|
||||
doDismissPopupWindow();
|
||||
});
|
||||
}
|
||||
|
||||
private void onMenuItemClicked(ToolbarMenuItem toolbarMenuItem) {
|
||||
runOnUiThread(() -> {
|
||||
if (mMenuItems == null || mMenuItemClickListener == null) {
|
||||
return;
|
||||
}
|
||||
MenuItem item = getMenuItemByToolbarMenuItem(toolbarMenuItem);
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"SelectionToolbarCallbackImpl onMenuItemClicked. toolbarMenuItem="
|
||||
+ toolbarMenuItem + " item=" + item);
|
||||
}
|
||||
// TODO: handle the menu item like clipboard
|
||||
if (item != null) {
|
||||
mMenuItemClickListener.onMenuItemClick(item);
|
||||
} else {
|
||||
Log.e(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"onMenuItemClicked: cannot find menu item.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class SelectionToolbarCallbackImpl extends ISelectionToolbarCallback.Stub {
|
||||
|
||||
private final WeakReference<RemoteFloatingToolbarPopup> mRemotePopup;
|
||||
|
||||
SelectionToolbarCallbackImpl(RemoteFloatingToolbarPopup popup) {
|
||||
mRemotePopup = new WeakReference<>(popup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShown(WidgetInfo info) {
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"SelectionToolbarCallbackImpl onShown: " + info);
|
||||
}
|
||||
final RemoteFloatingToolbarPopup remoteFloatingToolbarPopup = mRemotePopup.get();
|
||||
if (remoteFloatingToolbarPopup != null) {
|
||||
remoteFloatingToolbarPopup.onShow(info);
|
||||
} else {
|
||||
Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"Lost remoteFloatingToolbarPopup reference for onShown.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWidgetUpdated(WidgetInfo info) {
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"SelectionToolbarCallbackImpl onWidgetUpdated: info = " + info);
|
||||
}
|
||||
final RemoteFloatingToolbarPopup remoteFloatingToolbarPopup = mRemotePopup.get();
|
||||
if (remoteFloatingToolbarPopup != null) {
|
||||
remoteFloatingToolbarPopup.onWidgetUpdated(info);
|
||||
} else {
|
||||
Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"Lost remoteFloatingToolbarPopup reference for onWidgetUpdated.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolbarShowTimeout() {
|
||||
final RemoteFloatingToolbarPopup remoteFloatingToolbarPopup = mRemotePopup.get();
|
||||
if (remoteFloatingToolbarPopup != null) {
|
||||
remoteFloatingToolbarPopup.onToolbarShowTimeout();
|
||||
} else {
|
||||
Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"Lost remoteFloatingToolbarPopup reference for onToolbarShowTimeout.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMenuItemClicked(ToolbarMenuItem toolbarMenuItem) {
|
||||
final RemoteFloatingToolbarPopup remoteFloatingToolbarPopup = mRemotePopup.get();
|
||||
if (remoteFloatingToolbarPopup != null) {
|
||||
remoteFloatingToolbarPopup.onMenuItemClicked(toolbarMenuItem);
|
||||
} else {
|
||||
Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"Lost remoteFloatingToolbarPopup reference for onMenuItemClicked.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode) {
|
||||
if (DEBUG) {
|
||||
Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
|
||||
"SelectionToolbarCallbackImpl onError: " + errorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the identity of a MenuItem that is rendered in a FloatingToolbarPopup.
|
||||
*/
|
||||
static final class MenuItemRepr {
|
||||
|
||||
public final int mItemId;
|
||||
public final int mGroupId;
|
||||
@Nullable
|
||||
public final String mTitle;
|
||||
@Nullable private final Drawable mIcon;
|
||||
|
||||
private MenuItemRepr(
|
||||
int itemId, int groupId, @Nullable CharSequence title,
|
||||
@Nullable Drawable icon) {
|
||||
mItemId = itemId;
|
||||
mGroupId = groupId;
|
||||
mTitle = (title == null) ? null : title.toString();
|
||||
mIcon = icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of MenuItemRepr for the specified menu item.
|
||||
*/
|
||||
public static MenuItemRepr of(MenuItem menuItem) {
|
||||
return new MenuItemRepr(
|
||||
menuItem.getItemId(),
|
||||
menuItem.getGroupId(),
|
||||
menuItem.getTitle(),
|
||||
menuItem.getIcon());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this object's hashcode.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mItemId, mGroupId, mTitle, mIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this object is the same as the specified object.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof LocalFloatingToolbarPopup.MenuItemRepr)) {
|
||||
return false;
|
||||
}
|
||||
final MenuItemRepr other = (MenuItemRepr) o;
|
||||
return mItemId == other.mItemId
|
||||
&& mGroupId == other.mGroupId
|
||||
&& TextUtils.equals(mTitle, other.mTitle)
|
||||
// Many Drawables (icons) do not implement equals(). Using equals() here instead
|
||||
// of reference comparisons in case a Drawable subclass implements equals().
|
||||
&& Objects.equals(mIcon, other.mIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the two menu item collections are the same based on MenuItemRepr.
|
||||
*/
|
||||
public static boolean reprEquals(
|
||||
Collection<MenuItem> menuItems1, Collection<MenuItem> menuItems2) {
|
||||
if (menuItems1.size() != menuItems2.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Iterator<MenuItem> menuItems2Iter = menuItems2.iterator();
|
||||
for (MenuItem menuItem1 : menuItems1) {
|
||||
final MenuItem menuItem2 = menuItems2Iter.next();
|
||||
if (!MenuItemRepr.of(menuItem1).equals(
|
||||
MenuItemRepr.of(menuItem2))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user