blob: 4827f362c8e07349d2ee06954a8445f827453a2f [file] [log] [blame]
Jon Miranda228877d2021-02-09 11:05:00 -05001/*
2 * Copyright (C) 2021 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
17package com.android.launcher3;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.content.res.XmlResourceParser;
22import android.util.AttributeSet;
23import android.util.Log;
24import android.util.TypedValue;
25import android.util.Xml;
26
27import org.xmlpull.v1.XmlPullParser;
28import org.xmlpull.v1.XmlPullParserException;
29
30import java.io.IOException;
31import java.util.ArrayList;
32
33/**
34 * Workspace items have a fixed height, so we need a way to distribute any unused workspace height.
35 *
36 * The unused or "extra" height is allocated to three different variable heights:
37 * - The space above the workspace
38 * - The space between the workspace and hotseat
39 * - The espace below the hotseat
40 */
41public class DevicePaddings {
42
43 private static final String DEVICE_PADDING = "device-paddings";
44 private static final String DEVICE_PADDINGS = "device-padding";
45
46 private static final String WORKSPACE_TOP_PADDING = "workspaceTopPadding";
47 private static final String WORKSPACE_BOTTOM_PADDING = "workspaceBottomPadding";
48 private static final String HOTSEAT_BOTTOM_PADDING = "hotseatBottomPadding";
49
50 private static final String TAG = DevicePaddings.class.getSimpleName();
51 private static final boolean DEBUG = false;
52
53 ArrayList<DevicePadding> mDevicePaddings = new ArrayList<>();
54
55 public DevicePaddings(Context context) {
56 try (XmlResourceParser parser = context.getResources().getXml(R.xml.size_limits)) {
57 final int depth = parser.getDepth();
58 int type;
59 while (((type = parser.next()) != XmlPullParser.END_TAG ||
60 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
61 if ((type == XmlPullParser.START_TAG) && DEVICE_PADDING.equals(parser.getName())) {
62 final int displayDepth = parser.getDepth();
63 while (((type = parser.next()) != XmlPullParser.END_TAG ||
64 parser.getDepth() > displayDepth)
65 && type != XmlPullParser.END_DOCUMENT) {
66 if ((type == XmlPullParser.START_TAG)
67 && DEVICE_PADDINGS.equals(parser.getName())) {
68 TypedArray a = context.obtainStyledAttributes(
69 Xml.asAttributeSet(parser), R.styleable.DevicePadding);
70 int maxWidthPx = a.getDimensionPixelSize(
71 R.styleable.DevicePadding_maxEmptySpace, 0);
72 a.recycle();
73
74 PaddingFormula workspaceTopPadding = null;
75 PaddingFormula workspaceBottomPadding = null;
76 PaddingFormula hotseatBottomPadding = null;
77
78 final int limitDepth = parser.getDepth();
79 while (((type = parser.next()) != XmlPullParser.END_TAG ||
80 parser.getDepth() > limitDepth)
81 && type != XmlPullParser.END_DOCUMENT) {
82 AttributeSet attr = Xml.asAttributeSet(parser);
83 if ((type == XmlPullParser.START_TAG)) {
84 if (WORKSPACE_TOP_PADDING.equals(parser.getName())) {
85 workspaceTopPadding = new PaddingFormula(context, attr);
86 } else if (WORKSPACE_BOTTOM_PADDING.equals(parser.getName())) {
87 workspaceBottomPadding = new PaddingFormula(context, attr);
88 } else if (HOTSEAT_BOTTOM_PADDING.equals(parser.getName())) {
89 hotseatBottomPadding = new PaddingFormula(context, attr);
90 }
91 }
92 }
93
94 if (workspaceTopPadding == null
95 || workspaceBottomPadding == null
96 || hotseatBottomPadding == null) {
97 throw new RuntimeException("DevicePadding missing padding.");
98 }
99
100 mDevicePaddings.add(new DevicePadding(maxWidthPx, workspaceTopPadding,
101 workspaceBottomPadding, hotseatBottomPadding));
102 }
103 }
104 }
105 }
106 } catch (IOException | XmlPullParserException e) {
107 throw new RuntimeException(e);
108 }
109
110 // Sort ascending by maxEmptySpacePx
111 mDevicePaddings.sort((sl1, sl2) -> Integer.compare(sl1.maxEmptySpacePx,
112 sl2.maxEmptySpacePx));
113 }
114
115 public DevicePadding getDevicePadding(int extraSpacePx) {
116 for (DevicePadding limit : mDevicePaddings) {
117 if (extraSpacePx <= limit.maxEmptySpacePx) {
118 return limit;
119 }
120 }
121
122 return mDevicePaddings.get(mDevicePaddings.size() - 1);
123 }
124
125 /**
126 * Holds all the formulas to calculate the padding for a particular device based on the
127 * amount of extra space.
128 */
129 public static final class DevicePadding {
130
131 private final int maxEmptySpacePx;
132 private final PaddingFormula workspaceTopPadding;
133 private final PaddingFormula workspaceBottomPadding;
134 private final PaddingFormula hotseatBottomPadding;
135
136 public DevicePadding(int maxEmptySpacePx,
137 PaddingFormula workspaceTopPadding,
138 PaddingFormula workspaceBottomPadding,
139 PaddingFormula hotseatBottomPadding) {
140 this.maxEmptySpacePx = maxEmptySpacePx;
141 this.workspaceTopPadding = workspaceTopPadding;
142 this.workspaceBottomPadding = workspaceBottomPadding;
143 this.hotseatBottomPadding = hotseatBottomPadding;
144 }
145
146 public int getWorkspaceTopPadding(int extraSpacePx) {
147 return workspaceTopPadding.calculate(extraSpacePx);
148 }
149
150 public int getWorkspaceBottomPadding(int extraSpacePx) {
151 return workspaceBottomPadding.calculate(extraSpacePx);
152 }
153
154 public int getHotseatBottomPadding(int extraSpacePx) {
155 return hotseatBottomPadding.calculate(extraSpacePx);
156 }
157 }
158
159 /**
160 * Used to calculate a padding based on three variables: a, b, and c.
161 *
162 * Calculation: a * (extraSpace - c) + b
163 */
164 private static final class PaddingFormula {
165
166 private final float a;
167 private final float b;
168 private final float c;
169
170 public PaddingFormula(Context context, AttributeSet attrs) {
171 TypedArray t = context.obtainStyledAttributes(attrs,
172 R.styleable.DevicePaddingFormula);
173
174 a = getValue(t, R.styleable.DevicePaddingFormula_a);
175 b = getValue(t, R.styleable.DevicePaddingFormula_b);
176 c = getValue(t, R.styleable.DevicePaddingFormula_c);
177
178 t.recycle();
179 }
180
181 public int calculate(int extraSpacePx) {
182 if (DEBUG) {
183 Log.d(TAG, "a=" + a + " * (" + extraSpacePx + " - " + c + ") + b=" + b);
184 }
185 return Math.round(a * (extraSpacePx - c) + b);
186 }
187
188 private static float getValue(TypedArray a, int index) {
189 if (a.getType(index) == TypedValue.TYPE_DIMENSION) {
190 return a.getDimensionPixelSize(index, 0);
191 } else if (a.getType(index) == TypedValue.TYPE_FLOAT) {
192 return a.getFloat(index, 0);
193 }
194 return 0;
195 }
196
197 @Override
198 public String toString() {
199 return "a=" + a + ", b=" + b + ", c=" + c;
200 }
201 }
202}