1 /*
2  * Copyright (C) 2020 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 package com.android.launcher3.secondarydisplay;
17 
18 import static android.view.View.MeasureSpec.EXACTLY;
19 import static android.view.View.MeasureSpec.makeMeasureSpec;
20 
21 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
22 
23 import android.content.Context;
24 import android.util.AttributeSet;
25 import android.view.MotionEvent;
26 import android.view.View;
27 import android.widget.GridView;
28 
29 import com.android.launcher3.AbstractFloatingView;
30 import com.android.launcher3.BubbleTextView;
31 import com.android.launcher3.DeviceProfile;
32 import com.android.launcher3.DropTarget;
33 import com.android.launcher3.R;
34 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
35 import com.android.launcher3.config.FeatureFlags;
36 import com.android.launcher3.dragndrop.DragOptions;
37 import com.android.launcher3.dragndrop.DragView;
38 import com.android.launcher3.model.data.ItemInfo;
39 import com.android.launcher3.popup.PopupContainerWithArrow;
40 import com.android.launcher3.popup.PopupDataProvider;
41 import com.android.launcher3.popup.SystemShortcut;
42 import com.android.launcher3.util.ShortcutUtil;
43 import com.android.launcher3.util.TouchController;
44 import com.android.launcher3.views.BaseDragLayer;
45 
46 import java.util.ArrayList;
47 import java.util.List;
48 
49 /**
50  * DragLayer for Secondary launcher
51  */
52 public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> {
53 
54     private View mAllAppsButton;
55     private ActivityAllAppsContainerView<SecondaryDisplayLauncher> mAppsView;
56 
57     private GridView mWorkspace;
58     private PinnedAppsAdapter mPinnedAppsAdapter;
59 
SecondaryDragLayer(Context context, AttributeSet attrs)60     public SecondaryDragLayer(Context context, AttributeSet attrs) {
61         super(context, attrs, 1 /* alphaChannelCount */);
62         recreateControllers();
63     }
64 
65     @Override
recreateControllers()66     public void recreateControllers() {
67         mControllers = new TouchController[]{new CloseAllAppsTouchController(),
68                 mActivity.getDragController()};
69     }
70 
71     /**
72      * {@inheritDoc}
73      */
74     @Override
onFinishInflate()75     protected void onFinishInflate() {
76         super.onFinishInflate();
77         mAllAppsButton = findViewById(R.id.all_apps_button);
78 
79         mAppsView = findViewById(R.id.apps_view);
80         // Setup workspace
81         mWorkspace = findViewById(R.id.workspace_grid);
82         mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(),
83                 this::onIconLongClicked);
84         mWorkspace.setAdapter(mPinnedAppsAdapter);
85         mWorkspace.setNumColumns(mActivity.getDeviceProfile().inv.numColumns);
86     }
87 
88     /**
89      * {@inheritDoc}
90      */
91     @Override
onAttachedToWindow()92     protected void onAttachedToWindow() {
93         super.onAttachedToWindow();
94         mPinnedAppsAdapter.init();
95     }
96 
97     /**
98      * {@inheritDoc}
99      */
100     @Override
onDetachedFromWindow()101     protected void onDetachedFromWindow() {
102         super.onDetachedFromWindow();
103         mPinnedAppsAdapter.destroy();
104     }
105 
106     /**
107      * {@inheritDoc}
108      */
109     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)110     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
111         int width = MeasureSpec.getSize(widthMeasureSpec);
112         int height = MeasureSpec.getSize(heightMeasureSpec);
113         setMeasuredDimension(width, height);
114 
115         DeviceProfile grid = mActivity.getDeviceProfile();
116         int count = getChildCount();
117         for (int i = 0; i < count; i++) {
118             final View child = getChildAt(i);
119             if (child == mAppsView) {
120                 int horizontalPadding = (2 * grid.desiredWorkspaceHorizontalMarginPx)
121                         + grid.cellLayoutPaddingPx.left + grid.cellLayoutPaddingPx.right;
122                 int verticalPadding =
123                         grid.cellLayoutPaddingPx.top + grid.cellLayoutPaddingPx.bottom;
124 
125                 int maxWidth =
126                         grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + horizontalPadding;
127                 int appsWidth = Math.min(width - getPaddingLeft() - getPaddingRight(), maxWidth);
128 
129                 int maxHeight =
130                         grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + verticalPadding;
131                 int appsHeight = Math.min(height - getPaddingTop() - getPaddingBottom(), maxHeight);
132 
133                 mAppsView.measure(
134                         makeMeasureSpec(appsWidth, EXACTLY), makeMeasureSpec(appsHeight, EXACTLY));
135             } else if (child == mAllAppsButton) {
136                 int appsButtonSpec = makeMeasureSpec(grid.iconSizePx, EXACTLY);
137                 mAllAppsButton.measure(appsButtonSpec, appsButtonSpec);
138             } else if (child == mWorkspace) {
139                 measureChildWithMargins(mWorkspace, widthMeasureSpec, 0, heightMeasureSpec,
140                         grid.iconSizePx + grid.edgeMarginPx);
141             } else {
142                 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
143             }
144         }
145     }
146 
147     private class CloseAllAppsTouchController implements TouchController {
148 
149         @Override
onControllerTouchEvent(MotionEvent ev)150         public boolean onControllerTouchEvent(MotionEvent ev) {
151             return false;
152         }
153 
154         @Override
onControllerInterceptTouchEvent(MotionEvent ev)155         public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
156             if (!mActivity.isAppDrawerShown()) {
157                 return false;
158             }
159 
160             if (AbstractFloatingView.getTopOpenView(mActivity) != null) {
161                 return false;
162             }
163 
164             if (ev.getAction() == MotionEvent.ACTION_DOWN
165                     && !isEventOverView(mActivity.getAppsView(), ev)) {
166                 mActivity.showAppDrawer(false);
167                 return true;
168             }
169             return false;
170         }
171     }
172 
getPinnedAppsAdapter()173     public PinnedAppsAdapter getPinnedAppsAdapter() {
174         return mPinnedAppsAdapter;
175     }
176 
onIconLongClicked(View v)177     boolean onIconLongClicked(View v) {
178         if (!(v instanceof BubbleTextView)) {
179             return false;
180         }
181         if (PopupContainerWithArrow.getOpen(mActivity) != null) {
182             // There is already an items container open, so don't open this one.
183             v.clearFocus();
184             return false;
185         }
186         ItemInfo item = (ItemInfo) v.getTag();
187         if (!ShortcutUtil.supportsShortcuts(item)) {
188             return false;
189         }
190         PopupDataProvider popupDataProvider = mActivity.getPopupDataProvider();
191         if (popupDataProvider == null) {
192             return false;
193         }
194 
195         // order of this list will reflect in the popup
196         List<SystemShortcut> systemShortcuts = new ArrayList<>();
197         systemShortcuts.add(APP_INFO.getShortcut(mActivity, item, v));
198         // Hide redundant pin shortcut for app drawer icons if drag-n-drop is enabled.
199         if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
200             systemShortcuts.add(mPinnedAppsAdapter.getSystemShortcut(item, v));
201         }
202         int deepShortcutCount = popupDataProvider.getShortcutCountForItem(item);
203         final PopupContainerWithArrow<SecondaryDisplayLauncher> container;
204         container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
205                 R.layout.popup_container, mActivity.getDragLayer(), false);
206         container.populateAndShowRows((BubbleTextView) v, deepShortcutCount,
207                 systemShortcuts);
208         container.requestFocus();
209 
210         if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
211             return true;
212         }
213 
214         DragOptions options = new DragOptions();
215         DeviceProfile grid = mActivity.getDeviceProfile();
216         options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
217         options.preDragCondition = container.createPreDragCondition(false);
218         if (options.preDragCondition == null) {
219             options.preDragCondition = new DragOptions.PreDragCondition() {
220                 private DragView<SecondaryDisplayLauncher> mDragView;
221 
222                 @Override
223                 public boolean shouldStartDrag(double distanceDragged) {
224                     return mDragView != null && mDragView.isScaleAnimationFinished();
225                 }
226 
227                 @Override
228                 public void onPreDragStart(DropTarget.DragObject dragObject) {
229                     mDragView = dragObject.dragView;
230                     if (!shouldStartDrag(0)) {
231                         mDragView.setOnScaleAnimEndCallback(() ->
232                                 mActivity.beginDragShared(v, mActivity.getAppsView(), options));
233                     }
234                 }
235 
236                 @Override
237                 public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
238                     mDragView = null;
239                 }
240             };
241         }
242         mActivity.beginDragShared(v, mActivity.getAppsView(), options);
243         return true;
244     }
245 }
246