Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 838e36cd authored by Clara Bayarri's avatar Clara Bayarri
Browse files

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

This CL
- Creates a new FloatingActionMode
- Uses it for action mode requests of type floating
- Plumbs in the positioning invalidation logic

Change-Id: I379de5b0a87b256946d0a6d8014299cfb78e6734
parent 066be66d
Loading
Loading
Loading
Loading
+100 −9
Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@ import android.os.UserHandle;


import com.android.internal.R;
import com.android.internal.R;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.menu.ContextMenuBuilder;
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.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.DecorContentParent;
import com.android.internal.widget.DecorContentParent;
import com.android.internal.widget.FloatingToolbar;
import com.android.internal.widget.SwipeDismissLayout;
import com.android.internal.widget.SwipeDismissLayout;


import android.app.ActivityManager;
import android.app.ActivityManager;
@@ -2179,6 +2181,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
        private ActionBarContextView mPrimaryActionModeView;
        private ActionBarContextView mPrimaryActionModeView;
        private PopupWindow mPrimaryActionModePopup;
        private PopupWindow mPrimaryActionModePopup;
        private Runnable mShowPrimaryActionModePopup;
        private Runnable mShowPrimaryActionModePopup;
        private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
        private View mFloatingActionModeOriginatingView;
        private FloatingToolbar mFloatingToolbar;


        // View added at runtime to draw under the status bar area
        // View added at runtime to draw under the status bar area
        private View mStatusGuard;
        private View mStatusGuard;
@@ -2703,20 +2708,20 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                if (mode.getType() == ActionMode.TYPE_PRIMARY) {
                if (mode.getType() == ActionMode.TYPE_PRIMARY) {
                    cleanupPrimaryActionMode();
                    cleanupPrimaryActionMode();
                    mPrimaryActionMode = mode;
                    mPrimaryActionMode = mode;
                } else {
                } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
                    if (mFloatingActionMode != null) {
                        mFloatingActionMode.finish();
                    }
                    mFloatingActionMode = mode;
                    mFloatingActionMode = mode;
                }
                }
            } else {
            } else {
                if (type == ActionMode.TYPE_PRIMARY) {
                mode = createActionMode(type, wrappedCallback, originatingView);
                    cleanupPrimaryActionMode();
                if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
                    mode = createStandaloneActionMode(wrappedCallback);
                    setHandledActionMode(mode);
                    if (mode != null && callback.onCreateActionMode(mode, mode.getMenu())) {
                        setHandledPrimaryActionMode(mode);
                } else {
                } else {
                    mode = null;
                    mode = null;
                }
                }
            }
            }
            }
            if (mode != null && getCallback() != null && !isDestroyed()) {
            if (mode != null && getCallback() != null && !isDestroyed()) {
                try {
                try {
                    getCallback().onActionModeStarted(mode);
                    getCallback().onActionModeStarted(mode);
@@ -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() {
        public void startChanging() {
            mChanging = true;
            mChanging = true;
        }
        }
@@ -3128,6 +3148,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
            if (cb != null && !isDestroyed() && mFeatureId < 0) {
            if (cb != null && !isDestroyed() && mFeatureId < 0) {
                cb.onWindowFocusChanged(hasWindowFocus);
                cb.onWindowFocusChanged(hasWindowFocus);
            }
            }

            if (mFloatingToolbar != null) {
                if (hasWindowFocus) {
                    mFloatingToolbar.show();
                } else {
                    mFloatingToolbar.dismiss();
                }
            }
        }
        }


        void updateWindowResizeState() {
        void updateWindowResizeState() {
@@ -3179,6 +3207,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                }
                }
                mPrimaryActionModePopup = null;
                mPrimaryActionModePopup = null;
            }
            }
            if (mFloatingToolbar != null) {
                mFloatingToolbar.dismiss();
                mFloatingToolbar = null;
            }


            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (st != null && st.menu != null && mFeatureId < 0) {
            if (st != null && st.menu != null && mFeatureId < 0) {
@@ -3220,7 +3252,27 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
            updateColorViewTranslations();
            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) {
        private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
            cleanupPrimaryActionMode();
            if (mPrimaryActionModeView == null) {
            if (mPrimaryActionModeView == null) {
                if (isFloating()) {
                if (isFloating()) {
                    // Use the action bar theme.
                    // Use the action bar theme.
@@ -3291,6 +3343,35 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                    AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
                    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.
         * Clears out internal references when the action mode is destroyed.
         */
         */
@@ -3328,6 +3409,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                    }
                    }
                    mPrimaryActionMode = null;
                    mPrimaryActionMode = null;
                } else if (mode == mFloatingActionMode) {
                } else if (mode == mFloatingActionMode) {
                    cleanupFloatingActionModeViews();
                    mFloatingActionMode = null;
                    mFloatingActionMode = null;
                }
                }
                if (getCallback() != null && !isDestroyed()) {
                if (getCallback() != null && !isDestroyed()) {
@@ -3339,6 +3421,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                }
                }
                requestFitSystemWindows();
                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);
                }
            }
        }
        }
    }
    }


+141 −0
Original line number Original line Diff line number Diff line
/*
 * 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);
    }

}