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.InvariantDeviceProfile;
33 import com.android.launcher3.R;
34 import com.android.launcher3.allapps.AllAppsContainerView;
35 import com.android.launcher3.model.data.ItemInfo;
36 import com.android.launcher3.popup.PopupContainerWithArrow;
37 import com.android.launcher3.util.ShortcutUtil;
38 import com.android.launcher3.util.TouchController;
39 import com.android.launcher3.views.BaseDragLayer;
40 
41 import java.util.Arrays;
42 import java.util.Collections;
43 
44 /**
45  * DragLayer for Secondary launcher
46  */
47 public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> {
48 
49     private View mAllAppsButton;
50     private AllAppsContainerView mAppsView;
51 
52     private GridView mWorkspace;
53     private PinnedAppsAdapter mPinnedAppsAdapter;
54 
SecondaryDragLayer(Context context, AttributeSet attrs)55     public SecondaryDragLayer(Context context, AttributeSet attrs) {
56         super(context, attrs, 1 /* alphaChannelCount */);
57         recreateControllers();
58     }
59 
60     @Override
recreateControllers()61     public void recreateControllers() {
62         mControllers = new TouchController[] {new CloseAllAppsTouchController()};
63     }
64 
65     /**
66      * {@inheritDoc}
67      */
68     @Override
onFinishInflate()69     protected void onFinishInflate() {
70         super.onFinishInflate();
71         mAllAppsButton = findViewById(R.id.all_apps_button);
72 
73         mAppsView = findViewById(R.id.apps_view);
74         mAppsView.setOnIconLongClickListener(this::onIconLongClicked);
75 
76         // Setup workspace
77         mWorkspace = findViewById(R.id.workspace_grid);
78         mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(),
79                 this::onIconLongClicked);
80         mWorkspace.setAdapter(mPinnedAppsAdapter);
81         mWorkspace.setNumColumns(mActivity.getDeviceProfile().inv.numColumns);
82     }
83 
84     /**
85      * {@inheritDoc}
86      */
87     @Override
onAttachedToWindow()88     protected void onAttachedToWindow() {
89         super.onAttachedToWindow();
90         mPinnedAppsAdapter.init();
91     }
92 
93     /**
94      * {@inheritDoc}
95      */
96     @Override
onDetachedFromWindow()97     protected void onDetachedFromWindow() {
98         super.onDetachedFromWindow();
99         mPinnedAppsAdapter.destroy();
100     }
101 
102     /**
103      * {@inheritDoc}
104      */
105     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)106     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
107         int width = MeasureSpec.getSize(widthMeasureSpec);
108         int height = MeasureSpec.getSize(heightMeasureSpec);
109         setMeasuredDimension(width, height);
110 
111         DeviceProfile grid = mActivity.getDeviceProfile();
112         InvariantDeviceProfile idp = grid.inv;
113 
114         int count = getChildCount();
115         for (int i = 0; i < count; i++) {
116             final View child = getChildAt(i);
117             if (child == mAppsView) {
118                 int padding = 2 * (grid.desiredWorkspaceLeftRightMarginPx
119                         + grid.cellLayoutPaddingLeftRightPx);
120 
121                 int maxWidth = grid.allAppsCellWidthPx * idp.numAllAppsColumns + padding;
122                 int appsWidth = Math.min(width, maxWidth);
123 
124                 int maxHeight = grid.allAppsCellHeightPx * idp.numAllAppsColumns + padding;
125                 int appsHeight = Math.min(height, maxHeight);
126 
127                 mAppsView.measure(
128                         makeMeasureSpec(appsWidth, EXACTLY), makeMeasureSpec(appsHeight, EXACTLY));
129 
130             } else if (child == mAllAppsButton) {
131                 int appsButtonSpec = makeMeasureSpec(grid.iconSizePx, EXACTLY);
132                 mAllAppsButton.measure(appsButtonSpec, appsButtonSpec);
133 
134             } else if (child == mWorkspace) {
135                 measureChildWithMargins(mWorkspace, widthMeasureSpec, 0, heightMeasureSpec,
136                         grid.iconSizePx + grid.edgeMarginPx);
137 
138             } else {
139                 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
140             }
141         }
142     }
143 
144     private class CloseAllAppsTouchController implements TouchController {
145 
146         @Override
onControllerTouchEvent(MotionEvent ev)147         public boolean onControllerTouchEvent(MotionEvent ev) {
148             return false;
149         }
150 
151         @Override
onControllerInterceptTouchEvent(MotionEvent ev)152         public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
153             if (!mActivity.isAppDrawerShown()) {
154                 return false;
155             }
156 
157             if (AbstractFloatingView.getTopOpenView(mActivity) != null) {
158                 return false;
159             }
160 
161             if (ev.getAction() == MotionEvent.ACTION_DOWN
162                     && !isEventOverView(mActivity.getAppsView(), ev)) {
163                 mActivity.showAppDrawer(false);
164                 return true;
165             }
166             return false;
167         }
168     }
169 
onIconLongClicked(View v)170     private boolean onIconLongClicked(View v) {
171         if (!(v instanceof BubbleTextView)) {
172             return false;
173         }
174         if (PopupContainerWithArrow.getOpen(mActivity) != null) {
175             // There is already an items container open, so don't open this one.
176             v.clearFocus();
177             return false;
178         }
179         ItemInfo item = (ItemInfo) v.getTag();
180         if (!ShortcutUtil.supportsShortcuts(item)) {
181             return false;
182         }
183         final PopupContainerWithArrow container =
184                 (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
185                         R.layout.popup_container, mActivity.getDragLayer(), false);
186 
187         container.populateAndShow((BubbleTextView) v,
188                 mActivity.getPopupDataProvider().getShortcutCountForItem(item),
189                 Collections.emptyList(),
190                 Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item),
191                         APP_INFO.getShortcut(mActivity, item)));
192         v.getParent().requestDisallowInterceptTouchEvent(true);
193         return true;
194     }
195 }
196