Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher3;
     18 
     19 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
     20 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
     21 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
     22 
     23 import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
     24 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
     25 
     26 import android.annotation.SuppressLint;
     27 import android.content.Context;
     28 import android.support.annotation.IntDef;
     29 import android.util.AttributeSet;
     30 import android.util.Pair;
     31 import android.view.MotionEvent;
     32 import android.view.View;
     33 import android.widget.LinearLayout;
     34 
     35 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
     36 import com.android.launcher3.util.TouchController;
     37 import com.android.launcher3.views.BaseDragLayer;
     38 
     39 import java.lang.annotation.Retention;
     40 import java.lang.annotation.RetentionPolicy;
     41 
     42 /**
     43  * Base class for a View which shows a floating UI on top of the launcher UI.
     44  */
     45 public abstract class AbstractFloatingView extends LinearLayout implements TouchController {
     46 
     47     @IntDef(flag = true, value = {
     48             TYPE_FOLDER,
     49             TYPE_ACTION_POPUP,
     50             TYPE_WIDGETS_BOTTOM_SHEET,
     51             TYPE_WIDGET_RESIZE_FRAME,
     52             TYPE_WIDGETS_FULL_SHEET,
     53             TYPE_ON_BOARD_POPUP,
     54             TYPE_DISCOVERY_BOUNCE,
     55 
     56             TYPE_QUICKSTEP_PREVIEW,
     57             TYPE_TASK_MENU,
     58             TYPE_OPTIONS_POPUP
     59     })
     60     @Retention(RetentionPolicy.SOURCE)
     61     public @interface FloatingViewType {}
     62     public static final int TYPE_FOLDER = 1 << 0;
     63     public static final int TYPE_ACTION_POPUP = 1 << 1;
     64     public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2;
     65     public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3;
     66     public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4;
     67     public static final int TYPE_ON_BOARD_POPUP = 1 << 5;
     68     public static final int TYPE_DISCOVERY_BOUNCE = 1 << 6;
     69 
     70     // Popups related to quickstep UI
     71     public static final int TYPE_QUICKSTEP_PREVIEW = 1 << 6;
     72     public static final int TYPE_TASK_MENU = 1 << 7;
     73     public static final int TYPE_OPTIONS_POPUP = 1 << 8;
     74 
     75     public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
     76             | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
     77             | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
     78             | TYPE_OPTIONS_POPUP;
     79 
     80     // Type of popups which should be kept open during launcher rebind
     81     public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
     82             | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE;
     83 
     84     // Usually we show the back button when a floating view is open. Instead, hide for these types.
     85     public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE;
     86 
     87     public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE;
     88 
     89     protected boolean mIsOpen;
     90 
     91     public AbstractFloatingView(Context context, AttributeSet attrs) {
     92         super(context, attrs);
     93     }
     94 
     95     public AbstractFloatingView(Context context, AttributeSet attrs, int defStyleAttr) {
     96         super(context, attrs, defStyleAttr);
     97     }
     98 
     99     /**
    100      * We need to handle touch events to prevent them from falling through to the workspace below.
    101      */
    102     @SuppressLint("ClickableViewAccessibility")
    103     @Override
    104     public boolean onTouchEvent(MotionEvent ev) {
    105         return true;
    106     }
    107 
    108     public final void close(boolean animate) {
    109         animate &= !Utilities.isPowerSaverPreventingAnimation(getContext());
    110         if (mIsOpen) {
    111             BaseActivity.fromContext(getContext()).getUserEventDispatcher()
    112                     .resetElapsedContainerMillis("container closed");
    113         }
    114         handleClose(animate);
    115         mIsOpen = false;
    116     }
    117 
    118     protected abstract void handleClose(boolean animate);
    119 
    120     public abstract void logActionCommand(int command);
    121 
    122     public final boolean isOpen() {
    123         return mIsOpen;
    124     }
    125 
    126     protected void onWidgetsBound() {
    127     }
    128 
    129     protected abstract boolean isOfType(@FloatingViewType int type);
    130 
    131     /** @return Whether the back is consumed. If false, Launcher will handle the back as well. */
    132     public boolean onBackPressed() {
    133         logActionCommand(Action.Command.BACK);
    134         close(true);
    135         return true;
    136     }
    137 
    138     @Override
    139     public boolean onControllerTouchEvent(MotionEvent ev) {
    140         return false;
    141     }
    142 
    143     protected void announceAccessibilityChanges() {
    144         Pair<View, String> targetInfo = getAccessibilityTarget();
    145         if (targetInfo == null || !isAccessibilityEnabled(getContext())) {
    146             return;
    147         }
    148         sendCustomAccessibilityEvent(
    149                 targetInfo.first, TYPE_WINDOW_STATE_CHANGED, targetInfo.second);
    150 
    151         if (mIsOpen) {
    152             sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
    153         }
    154         BaseDraggingActivity.fromContext(getContext()).getDragLayer()
    155                 .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
    156     }
    157 
    158     protected Pair<View, String> getAccessibilityTarget() {
    159         return null;
    160     }
    161 
    162     protected static <T extends AbstractFloatingView> T getOpenView(
    163             BaseDraggingActivity activity, @FloatingViewType int type) {
    164         BaseDragLayer dragLayer = activity.getDragLayer();
    165         // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
    166         // and will be one of the last views.
    167         for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
    168             View child = dragLayer.getChildAt(i);
    169             if (child instanceof AbstractFloatingView) {
    170                 AbstractFloatingView view = (AbstractFloatingView) child;
    171                 if (view.isOfType(type) && view.isOpen()) {
    172                     return (T) view;
    173                 }
    174             }
    175         }
    176         return null;
    177     }
    178 
    179     public static void closeOpenContainer(BaseDraggingActivity activity,
    180             @FloatingViewType int type) {
    181         AbstractFloatingView view = getOpenView(activity, type);
    182         if (view != null) {
    183             view.close(true);
    184         }
    185     }
    186 
    187     public static void closeOpenViews(BaseDraggingActivity activity, boolean animate,
    188             @FloatingViewType int type) {
    189         BaseDragLayer dragLayer = activity.getDragLayer();
    190         // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
    191         // and will be one of the last views.
    192         for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
    193             View child = dragLayer.getChildAt(i);
    194             if (child instanceof AbstractFloatingView) {
    195                 AbstractFloatingView abs = (AbstractFloatingView) child;
    196                 if (abs.isOfType(type)) {
    197                     abs.close(animate);
    198                 }
    199             }
    200         }
    201     }
    202 
    203     public static void closeAllOpenViews(BaseDraggingActivity activity, boolean animate) {
    204         closeOpenViews(activity, animate, TYPE_ALL);
    205         activity.finishAutoCancelActionMode();
    206     }
    207 
    208     public static void closeAllOpenViews(BaseDraggingActivity activity) {
    209         closeAllOpenViews(activity, true);
    210     }
    211 
    212     public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) {
    213         return getTopOpenViewWithType(activity, TYPE_ALL);
    214     }
    215 
    216     public static AbstractFloatingView getTopOpenViewWithType(BaseDraggingActivity activity,
    217             @FloatingViewType int type) {
    218         return getOpenView(activity, type);
    219     }
    220 }
    221