blob: a2c52554fc7825b33835b72d1fe078b26c9f0b00 [file] [log] [blame]
Sebastian Franco09589322022-11-02 15:25:58 -07001/*
2 * Copyright (C) 2022 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 */
16package com.android.launcher3;
17
18import android.content.Context;
19import android.graphics.Canvas;
20import android.graphics.Rect;
21import android.graphics.drawable.Drawable;
22import android.util.AttributeSet;
23import android.view.View;
24
25import com.android.launcher3.celllayout.CellLayoutLayoutParams;
26import com.android.launcher3.util.CellAndSpan;
27import com.android.launcher3.util.GridOccupancy;
28
29import java.util.function.Supplier;
30
31/**
32 * CellLayout that simulates a split in the middle for use in foldable devices.
33 */
34public class MultipageCellLayout extends CellLayout {
35
36 private final Drawable mLeftBackground;
37 private final Drawable mRightBackground;
38
39 private View mSeam;
40
Sebastian Franco317c4fa2023-02-21 17:01:24 -080041 private boolean mSeamWasAdded = false;
42
Sebastian Franco09589322022-11-02 15:25:58 -070043 public MultipageCellLayout(Context context) {
44 this(context, null);
45 }
46
47 public MultipageCellLayout(Context context, AttributeSet attrs) {
48 this(context, attrs, 0);
49 }
50
51 public MultipageCellLayout(Context context, AttributeSet attrs, int defStyle) {
52 super(context, attrs, defStyle);
53 mLeftBackground = getContext().getDrawable(R.drawable.bg_celllayout);
54 mLeftBackground.setCallback(this);
55 mLeftBackground.setAlpha(0);
56
57 mRightBackground = getContext().getDrawable(R.drawable.bg_celllayout);
58 mRightBackground.setCallback(this);
59 mRightBackground.setAlpha(0);
60
61 DeviceProfile deviceProfile = mActivity.getDeviceProfile();
62
63 mCountX = deviceProfile.inv.numColumns * 2;
64 mCountY = deviceProfile.inv.numRows;
65 mSeam = new View(getContext());
66 setGridSize(mCountX, mCountY);
67 }
68
69 @Override
Sebastian Franco317c4fa2023-02-21 17:01:24 -080070 boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY, View dragView,
71 int[] direction, boolean commit) {
72 return simulateSeam(
73 () -> super.createAreaForResize(cellX, cellY, spanX, spanY, dragView, direction,
74 commit));
75 }
76
77 @Override
78 void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
79 simulateSeam(() -> {
80 super.regionToCenterPoint(cellX, cellY, spanX, spanY, result);
81 return 0;
82 });
83 }
84
85 @Override
Sebastian Franco09589322022-11-02 15:25:58 -070086 ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int minSpanX, int minSpanY,
87 int spanX, int spanY) {
Sebastian Franco317c4fa2023-02-21 17:01:24 -080088 return removeSeamFromSolution(simulateSeam(
Sebastian Franco09589322022-11-02 15:25:58 -070089 () -> super.closestEmptySpaceReorder(pixelX, pixelY, minSpanX, minSpanY, spanX,
Sebastian Franco317c4fa2023-02-21 17:01:24 -080090 spanY)));
Sebastian Franco09589322022-11-02 15:25:58 -070091 }
92
93 @Override
94 protected ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
95 int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
96 ItemConfiguration solution) {
Sebastian Franco317c4fa2023-02-21 17:01:24 -080097 return removeSeamFromSolution(simulateSeam(
Sebastian Franco09589322022-11-02 15:25:58 -070098 () -> super.findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
Sebastian Franco317c4fa2023-02-21 17:01:24 -080099 direction, dragView, decX, solution)));
Sebastian Franco09589322022-11-02 15:25:58 -0700100 }
101
102 @Override
103 public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX, int spanY,
104 View dragView) {
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800105 return removeSeamFromSolution(simulateSeam(
106 () -> super.dropInPlaceSolution(pixelX, pixelY, spanX, spanY, dragView)));
Sebastian Franco09589322022-11-02 15:25:58 -0700107 }
108
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800109 void addSeam() {
Sebastian Franco09589322022-11-02 15:25:58 -0700110 CellLayoutLayoutParams lp = new CellLayoutLayoutParams(mCountX / 2, 0, 1, mCountY);
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800111 mSeamWasAdded = true;
Sebastian Franco09589322022-11-02 15:25:58 -0700112 lp.canReorder = false;
113 mCountX++;
114 mShortcutsAndWidgets.addViewInLayout(mSeam, lp);
Sebastian Franco09589322022-11-02 15:25:58 -0700115 mOccupied = createGridOccupancy();
116 mTmpOccupied = new GridOccupancy(mCountX, mCountY);
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800117 }
Sebastian Franco09589322022-11-02 15:25:58 -0700118
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800119 void removeSeam() {
Sebastian Franco09589322022-11-02 15:25:58 -0700120 mCountX--;
121 mShortcutsAndWidgets.removeViewInLayout(mSeam);
Sebastian Franco09589322022-11-02 15:25:58 -0700122 mTmpOccupied = new GridOccupancy(mCountX, mCountY);
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800123 mSeamWasAdded = false;
124 }
125
126 protected <T> T simulateSeam(Supplier<T> f) {
127 if (mSeamWasAdded) {
128 return f.get();
129 }
130 GridOccupancy auxGrid = mOccupied;
131 addSeam();
132 T res = f.get();
133 removeSeam();
134 mOccupied = auxGrid;
Sebastian Franco09589322022-11-02 15:25:58 -0700135 return res;
136 }
137
138 private ItemConfiguration removeSeamFromSolution(ItemConfiguration solution) {
139 solution.map.forEach((view, cell) -> cell.cellX = cell.cellX > mCountX / 2
140 ? cell.cellX - 1 : cell.cellX);
141 solution.cellX = solution.cellX > mCountX / 2 ? solution.cellX - 1 : solution.cellX;
142 return solution;
143 }
144
145 GridOccupancy createGridOccupancy() {
146 GridOccupancy grid = new GridOccupancy(mCountX, mCountY);
147 for (int i = 0; i < mShortcutsAndWidgets.getChildCount(); i++) {
148 View view = mShortcutsAndWidgets.getChildAt(i);
149 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams();
150 int seamOffset = lp.getCellX() >= mCountX / 2 && lp.canReorder ? 1 : 0;
151 grid.markCells(lp.getCellX() + seamOffset, lp.getCellY(), lp.cellHSpan, lp.cellVSpan,
152 true);
153 }
154 return grid;
155 }
156
157 @Override
158 protected void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
159 int childCount = mShortcutsAndWidgets.getChildCount();
160 for (int i = 0; i < childCount; i++) {
161 View child = mShortcutsAndWidgets.getChildAt(i);
162 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
163 int seamOffset = lp.getCellX() >= mCountX / 2 && lp.canReorder ? 1 : 0;
164 CellAndSpan c = new CellAndSpan(lp.getCellX() + seamOffset, lp.getCellY(), lp.cellHSpan,
165 lp.cellVSpan);
166 solution.add(child, c);
167 }
168 }
169
170 @Override
171 protected void onDraw(Canvas canvas) {
172 if (mLeftBackground.getAlpha() > 0) {
173 mLeftBackground.setState(mBackground.getState());
174 mLeftBackground.draw(canvas);
175 }
176 if (mRightBackground.getAlpha() > 0) {
177 mRightBackground.setState(mBackground.getState());
178 mRightBackground.draw(canvas);
179 }
180
181 super.onDraw(canvas);
182 }
183
184 @Override
185 protected void updateBgAlpha() {
186 mLeftBackground.setAlpha((int) (mSpringLoadedProgress * 255));
187 mRightBackground.setAlpha((int) (mSpringLoadedProgress * 255));
188 }
189
190 @Override
191 protected void onLayout(boolean changed, int l, int t, int r, int b) {
192 super.onLayout(changed, l, t, r, b);
193 Rect rect = mBackground.getBounds();
194 mLeftBackground.setBounds(rect.left, rect.top, rect.right / 2 - 20, rect.bottom);
195 mRightBackground.setBounds(rect.right / 2 + 20, rect.top, rect.right, rect.bottom);
196 }
197}