Merge "Floating toolbar: Create the FloatingActionMode and use it in DecorView."

This commit is contained in:
Clara Bayarri
2015-04-08 07:59:58 +00:00
committed by Android (Google) Code Review
2 changed files with 241 additions and 9 deletions

View File

@@ -29,6 +29,7 @@ import android.os.UserHandle;
import com.android.internal.R;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.menu.ContextMenuBuilder;
@@ -41,6 +42,7 @@ import com.android.internal.view.menu.MenuView;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.DecorContentParent;
import com.android.internal.widget.FloatingToolbar;
import com.android.internal.widget.SwipeDismissLayout;
import android.app.ActivityManager;
@@ -2179,6 +2181,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private ActionBarContextView mPrimaryActionModeView;
private PopupWindow mPrimaryActionModePopup;
private Runnable mShowPrimaryActionModePopup;
private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
private View mFloatingActionModeOriginatingView;
private FloatingToolbar mFloatingToolbar;
// View added at runtime to draw under the status bar area
private View mStatusGuard;
@@ -2703,18 +2708,18 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (mode.getType() == ActionMode.TYPE_PRIMARY) {
cleanupPrimaryActionMode();
mPrimaryActionMode = mode;
} else {
} else if (mode.getType() == ActionMode.TYPE_FLOATING) {
if (mFloatingActionMode != null) {
mFloatingActionMode.finish();
}
mFloatingActionMode = mode;
}
} else {
if (type == ActionMode.TYPE_PRIMARY) {
cleanupPrimaryActionMode();
mode = createStandaloneActionMode(wrappedCallback);
if (mode != null && callback.onCreateActionMode(mode, mode.getMenu())) {
setHandledPrimaryActionMode(mode);
} else {
mode = null;
}
mode = createActionMode(type, wrappedCallback, originatingView);
if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
setHandledActionMode(mode);
} else {
mode = null;
}
}
if (mode != null && getCallback() != null && !isDestroyed()) {
@@ -2737,6 +2742,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
private void cleanupFloatingActionModeViews() {
if (mFloatingToolbar != null) {
mFloatingToolbar.dismiss();
mFloatingToolbar = null;
}
if (mFloatingActionModeOriginatingView != null) {
if (mFloatingToolbarPreDrawListener != null) {
mFloatingActionModeOriginatingView.getViewTreeObserver()
.removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
mFloatingToolbarPreDrawListener = null;
}
mFloatingActionModeOriginatingView = null;
}
}
public void startChanging() {
mChanging = true;
}
@@ -3128,6 +3148,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (cb != null && !isDestroyed() && mFeatureId < 0) {
cb.onWindowFocusChanged(hasWindowFocus);
}
if (mFloatingToolbar != null) {
if (hasWindowFocus) {
mFloatingToolbar.show();
} else {
mFloatingToolbar.dismiss();
}
}
}
void updateWindowResizeState() {
@@ -3179,6 +3207,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
mPrimaryActionModePopup = null;
}
if (mFloatingToolbar != null) {
mFloatingToolbar.dismiss();
mFloatingToolbar = null;
}
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
if (st != null && st.menu != null && mFeatureId < 0) {
@@ -3220,7 +3252,27 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
updateColorViewTranslations();
}
private ActionMode createActionMode(
int type, ActionMode.Callback2 callback, View originatingView) {
switch (type) {
case ActionMode.TYPE_PRIMARY:
default:
return createStandaloneActionMode(callback);
case ActionMode.TYPE_FLOATING:
return createFloatingActionMode(originatingView, callback);
}
}
private void setHandledActionMode(ActionMode mode) {
if (mode.getType() == ActionMode.TYPE_PRIMARY) {
setHandledPrimaryActionMode(mode);
} else if (mode.getType() == ActionMode.TYPE_FLOATING) {
setHandledFloatingActionMode(mode);
}
}
private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
cleanupPrimaryActionMode();
if (mPrimaryActionModeView == null) {
if (isFloating()) {
// Use the action bar theme.
@@ -3291,6 +3343,35 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
private ActionMode createFloatingActionMode(
View originatingView, ActionMode.Callback2 callback) {
if (mFloatingActionMode != null) {
mFloatingActionMode.finish();
}
cleanupFloatingActionModeViews();
mFloatingToolbar = new FloatingToolbar(mContext, PhoneWindow.this);
final FloatingActionMode mode = new FloatingActionMode(
mContext, callback, originatingView, mFloatingToolbar);
mFloatingActionModeOriginatingView = originatingView;
mFloatingToolbarPreDrawListener =
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mode.updateViewLocationInWindow();
return true;
}
};
return mode;
}
private void setHandledFloatingActionMode(ActionMode mode) {
mFloatingActionMode = mode;
mFloatingActionMode.invalidate();
mFloatingToolbar.show();
mFloatingActionModeOriginatingView.getViewTreeObserver()
.addOnPreDrawListener(mFloatingToolbarPreDrawListener);
}
/**
* Clears out internal references when the action mode is destroyed.
*/
@@ -3328,6 +3409,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
mPrimaryActionMode = null;
} else if (mode == mFloatingActionMode) {
cleanupFloatingActionModeViews();
mFloatingActionMode = null;
}
if (getCallback() != null && !isDestroyed()) {
@@ -3339,6 +3421,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
requestFitSystemWindows();
}
@Override
public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
if (mWrapped instanceof ActionMode.Callback2) {
((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
} else {
super.onGetContentRect(mode, view, outRect);
}
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.view;
import android.content.Context;
import android.graphics.Rect;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.widget.FloatingToolbar;
public class FloatingActionMode extends ActionMode {
private final Context mContext;
private final ActionMode.Callback2 mCallback;
private final MenuBuilder mMenu;
private final FloatingToolbar mFloatingToolbar;
private final Rect mContentRect;
private final Rect mContentRectOnWindow;
private final Rect mPreviousContentRectOnWindow;
private final int[] mViewPosition;
private final View mOriginatingView;
public FloatingActionMode(
Context context, ActionMode.Callback2 callback, View originatingView,
FloatingToolbar floatingToolbar) {
mContext = context;
mCallback = callback;
mMenu = new MenuBuilder(context).setDefaultShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM);
mFloatingToolbar = floatingToolbar
.setMenu(mMenu)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
return mCallback.onActionItemClicked(FloatingActionMode.this, item);
}
});
setType(ActionMode.TYPE_FLOATING);
mContentRect = new Rect();
mContentRectOnWindow = new Rect();
mPreviousContentRectOnWindow = new Rect();
mViewPosition = new int[2];
mOriginatingView = originatingView;
}
@Override
public void setTitle(CharSequence title) {}
@Override
public void setTitle(int resId) {}
@Override
public void setSubtitle(CharSequence subtitle) {}
@Override
public void setSubtitle(int resId) {}
@Override
public void setCustomView(View view) {}
@Override
public void invalidate() {
mCallback.onPrepareActionMode(this, mMenu);
mFloatingToolbar.updateLayout();
invalidateContentRect();
}
@Override
public void invalidateContentRect() {
mCallback.onGetContentRect(this, mOriginatingView, mContentRect);
repositionToolbar();
}
public void updateViewLocationInWindow() {
mOriginatingView.getLocationInWindow(mViewPosition);
repositionToolbar();
}
private void repositionToolbar() {
mContentRectOnWindow.set(
mContentRect.left + mViewPosition[0],
mContentRect.top + mViewPosition[1],
mContentRect.right + mViewPosition[0],
mContentRect.bottom + mViewPosition[1]);
if (!mContentRectOnWindow.equals(mPreviousContentRectOnWindow)) {
mFloatingToolbar.setContentRect(mContentRectOnWindow);
mFloatingToolbar.updateLayout();
}
mPreviousContentRectOnWindow.set(mContentRectOnWindow);
}
@Override
public void finish() {
mCallback.onDestroyActionMode(this);
}
@Override
public Menu getMenu() {
return mMenu;
}
@Override
public CharSequence getTitle() {
return null;
}
@Override
public CharSequence getSubtitle() {
return null;
}
@Override
public View getCustomView() {
return null;
}
@Override
public MenuInflater getMenuInflater() {
return new MenuInflater(mContext);
}
}