237 lines
9.9 KiB
Java
237 lines
9.9 KiB
Java
|
/*
|
||
|
* Copyright (C) 2021 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package com.android.launcher3;
|
||
|
|
||
|
import android.content.Context;
|
||
|
import android.content.res.TypedArray;
|
||
|
import android.content.res.XmlResourceParser;
|
||
|
import android.util.AttributeSet;
|
||
|
import android.util.Log;
|
||
|
import android.util.TypedValue;
|
||
|
import android.util.Xml;
|
||
|
|
||
|
import org.xmlpull.v1.XmlPullParser;
|
||
|
import org.xmlpull.v1.XmlPullParserException;
|
||
|
|
||
|
import java.io.IOException;
|
||
|
import java.util.ArrayList;
|
||
|
|
||
|
/**
|
||
|
* Workspace items have a fixed height, so we need a way to distribute any unused workspace height.
|
||
|
*
|
||
|
* The unused or "extra" height is allocated to three different variable heights:
|
||
|
* - The space above the workspace
|
||
|
* - The space between the workspace and hotseat
|
||
|
* - The space below the hotseat
|
||
|
*/
|
||
|
public class DevicePaddings {
|
||
|
|
||
|
private static final String DEVICE_PADDINGS = "device-paddings";
|
||
|
private static final String DEVICE_PADDING = "device-padding";
|
||
|
|
||
|
private static final String WORKSPACE_TOP_PADDING = "workspaceTopPadding";
|
||
|
private static final String WORKSPACE_BOTTOM_PADDING = "workspaceBottomPadding";
|
||
|
private static final String HOTSEAT_BOTTOM_PADDING = "hotseatBottomPadding";
|
||
|
|
||
|
private static final String TAG = DevicePaddings.class.getSimpleName();
|
||
|
private static final boolean DEBUG = false;
|
||
|
|
||
|
ArrayList<DevicePadding> mDevicePaddings = new ArrayList<>();
|
||
|
|
||
|
public DevicePaddings(Context context, int devicePaddingId) {
|
||
|
try (XmlResourceParser parser = context.getResources().getXml(devicePaddingId)) {
|
||
|
final int depth = parser.getDepth();
|
||
|
int type;
|
||
|
while (((type = parser.next()) != XmlPullParser.END_TAG ||
|
||
|
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
|
||
|
if ((type == XmlPullParser.START_TAG) && DEVICE_PADDINGS.equals(parser.getName())) {
|
||
|
final int displayDepth = parser.getDepth();
|
||
|
while (((type = parser.next()) != XmlPullParser.END_TAG ||
|
||
|
parser.getDepth() > displayDepth)
|
||
|
&& type != XmlPullParser.END_DOCUMENT) {
|
||
|
if ((type == XmlPullParser.START_TAG)
|
||
|
&& DEVICE_PADDING.equals(parser.getName())) {
|
||
|
TypedArray a = context.obtainStyledAttributes(
|
||
|
Xml.asAttributeSet(parser), R.styleable.DevicePadding);
|
||
|
int maxWidthPx = a.getDimensionPixelSize(
|
||
|
R.styleable.DevicePadding_maxEmptySpace, 0);
|
||
|
a.recycle();
|
||
|
|
||
|
PaddingFormula workspaceTopPadding = null;
|
||
|
PaddingFormula workspaceBottomPadding = null;
|
||
|
PaddingFormula hotseatBottomPadding = null;
|
||
|
|
||
|
final int limitDepth = parser.getDepth();
|
||
|
while (((type = parser.next()) != XmlPullParser.END_TAG ||
|
||
|
parser.getDepth() > limitDepth)
|
||
|
&& type != XmlPullParser.END_DOCUMENT) {
|
||
|
AttributeSet attr = Xml.asAttributeSet(parser);
|
||
|
if ((type == XmlPullParser.START_TAG)) {
|
||
|
if (WORKSPACE_TOP_PADDING.equals(parser.getName())) {
|
||
|
workspaceTopPadding = new PaddingFormula(context, attr);
|
||
|
} else if (WORKSPACE_BOTTOM_PADDING.equals(parser.getName())) {
|
||
|
workspaceBottomPadding = new PaddingFormula(context, attr);
|
||
|
} else if (HOTSEAT_BOTTOM_PADDING.equals(parser.getName())) {
|
||
|
hotseatBottomPadding = new PaddingFormula(context, attr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (workspaceTopPadding == null
|
||
|
|| workspaceBottomPadding == null
|
||
|
|| hotseatBottomPadding == null) {
|
||
|
if (Utilities.IS_DEBUG_DEVICE) {
|
||
|
throw new RuntimeException("DevicePadding missing padding.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DevicePadding dp = new DevicePadding(maxWidthPx, workspaceTopPadding,
|
||
|
workspaceBottomPadding, hotseatBottomPadding);
|
||
|
if (dp.isValid()) {
|
||
|
mDevicePaddings.add(dp);
|
||
|
} else {
|
||
|
Log.e(TAG, "Invalid device padding found.");
|
||
|
if (Utilities.IS_DEBUG_DEVICE) {
|
||
|
throw new RuntimeException("DevicePadding is invalid");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} catch (IOException | XmlPullParserException e) {
|
||
|
Log.e(TAG, "Failure parsing device padding layout.", e);
|
||
|
throw new RuntimeException(e);
|
||
|
}
|
||
|
|
||
|
// Sort ascending by maxEmptySpacePx
|
||
|
mDevicePaddings.sort((sl1, sl2) -> Integer.compare(sl1.maxEmptySpacePx,
|
||
|
sl2.maxEmptySpacePx));
|
||
|
}
|
||
|
|
||
|
public DevicePadding getDevicePadding(int extraSpacePx) {
|
||
|
for (DevicePadding limit : mDevicePaddings) {
|
||
|
if (extraSpacePx <= limit.maxEmptySpacePx) {
|
||
|
return limit;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return mDevicePaddings.get(mDevicePaddings.size() - 1);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Holds all the formulas to calculate the padding for a particular device based on the
|
||
|
* amount of extra space.
|
||
|
*/
|
||
|
public static final class DevicePadding {
|
||
|
|
||
|
// One for each padding since they can each be off by 1 due to rounding errors.
|
||
|
private static final int ROUNDING_THRESHOLD_PX = 3;
|
||
|
|
||
|
private final int maxEmptySpacePx;
|
||
|
private final PaddingFormula workspaceTopPadding;
|
||
|
private final PaddingFormula workspaceBottomPadding;
|
||
|
private final PaddingFormula hotseatBottomPadding;
|
||
|
|
||
|
public DevicePadding(int maxEmptySpacePx,
|
||
|
PaddingFormula workspaceTopPadding,
|
||
|
PaddingFormula workspaceBottomPadding,
|
||
|
PaddingFormula hotseatBottomPadding) {
|
||
|
this.maxEmptySpacePx = maxEmptySpacePx;
|
||
|
this.workspaceTopPadding = workspaceTopPadding;
|
||
|
this.workspaceBottomPadding = workspaceBottomPadding;
|
||
|
this.hotseatBottomPadding = hotseatBottomPadding;
|
||
|
}
|
||
|
|
||
|
public int getMaxEmptySpacePx() {
|
||
|
return maxEmptySpacePx;
|
||
|
}
|
||
|
|
||
|
public int getWorkspaceTopPadding(int extraSpacePx) {
|
||
|
return workspaceTopPadding.calculate(extraSpacePx);
|
||
|
}
|
||
|
|
||
|
public int getWorkspaceBottomPadding(int extraSpacePx) {
|
||
|
return workspaceBottomPadding.calculate(extraSpacePx);
|
||
|
}
|
||
|
|
||
|
public int getHotseatBottomPadding(int extraSpacePx) {
|
||
|
return hotseatBottomPadding.calculate(extraSpacePx);
|
||
|
}
|
||
|
|
||
|
public boolean isValid() {
|
||
|
int workspaceTopPadding = getWorkspaceTopPadding(maxEmptySpacePx);
|
||
|
int workspaceBottomPadding = getWorkspaceBottomPadding(maxEmptySpacePx);
|
||
|
int hotseatBottomPadding = getHotseatBottomPadding(maxEmptySpacePx);
|
||
|
int sum = workspaceTopPadding + workspaceBottomPadding + hotseatBottomPadding;
|
||
|
int diff = Math.abs(sum - maxEmptySpacePx);
|
||
|
if (DEBUG) {
|
||
|
Log.d(TAG, "isValid: workspaceTopPadding=" + workspaceTopPadding
|
||
|
+ ", workspaceBottomPadding=" + workspaceBottomPadding
|
||
|
+ ", hotseatBottomPadding=" + hotseatBottomPadding
|
||
|
+ ", sum=" + sum
|
||
|
+ ", diff=" + diff);
|
||
|
}
|
||
|
return diff <= ROUNDING_THRESHOLD_PX;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used to calculate a padding based on three variables: a, b, and c.
|
||
|
*
|
||
|
* Calculation: a * (extraSpace - c) + b
|
||
|
*/
|
||
|
private static final class PaddingFormula {
|
||
|
|
||
|
private final float a;
|
||
|
private final float b;
|
||
|
private final float c;
|
||
|
|
||
|
public PaddingFormula(Context context, AttributeSet attrs) {
|
||
|
TypedArray t = context.obtainStyledAttributes(attrs,
|
||
|
R.styleable.DevicePaddingFormula);
|
||
|
|
||
|
a = getValue(t, R.styleable.DevicePaddingFormula_a);
|
||
|
b = getValue(t, R.styleable.DevicePaddingFormula_b);
|
||
|
c = getValue(t, R.styleable.DevicePaddingFormula_c);
|
||
|
|
||
|
t.recycle();
|
||
|
}
|
||
|
|
||
|
public int calculate(int extraSpacePx) {
|
||
|
if (DEBUG) {
|
||
|
Log.d(TAG, "a=" + a + " * (" + extraSpacePx + " - " + c + ") + b=" + b);
|
||
|
}
|
||
|
return Math.round(a * (extraSpacePx - c) + b);
|
||
|
}
|
||
|
|
||
|
private static float getValue(TypedArray a, int index) {
|
||
|
if (a.getType(index) == TypedValue.TYPE_DIMENSION) {
|
||
|
return a.getDimensionPixelSize(index, 0);
|
||
|
} else if (a.getType(index) == TypedValue.TYPE_FLOAT) {
|
||
|
return a.getFloat(index, 0);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return "a=" + a + ", b=" + b + ", c=" + c;
|
||
|
}
|
||
|
}
|
||
|
}
|