Snap for 10296703 from a6a71d5ace to udc-qpr1-release
Change-Id: I0602922e4f584b88e59ac81bae0d4b77332939b9
This commit is contained in:
@@ -2042,10 +2042,5 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
setCrossWindowBlursEnabled(
|
||||
CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSurface(SurfaceControl surface) {
|
||||
super.setSurface(surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,12 @@ public class DepthController extends BaseDepthController implements StateHandler
|
||||
super.applyDepthAndBlur();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInvalidSurface() {
|
||||
// Lets wait for surface to become valid again
|
||||
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
|
||||
mIgnoreStateChangesDuringMultiWindowAnimation = true;
|
||||
|
||||
@@ -41,8 +41,10 @@ public class TaskbarAutohideSuspendController implements
|
||||
public static final int FLAG_AUTOHIDE_SUSPEND_TOUCHING = 1 << 2;
|
||||
// Taskbar EDU overlay is open above the Taskbar. */
|
||||
public static final int FLAG_AUTOHIDE_SUSPEND_EDU_OPEN = 1 << 3;
|
||||
// Taskbar in immersive mode in overview
|
||||
// Taskbar is in immersive mode in overview.
|
||||
public static final int FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER = 1 << 4;
|
||||
// Transient Taskbar is temporarily unstashed (pending a timeout).
|
||||
public static final int FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR = 1 << 5;
|
||||
|
||||
@IntDef(flag = true, value = {
|
||||
FLAG_AUTOHIDE_SUSPEND_FULLSCREEN,
|
||||
@@ -50,6 +52,7 @@ public class TaskbarAutohideSuspendController implements
|
||||
FLAG_AUTOHIDE_SUSPEND_TOUCHING,
|
||||
FLAG_AUTOHIDE_SUSPEND_EDU_OPEN,
|
||||
FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER,
|
||||
FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface AutohideSuspendFlag {}
|
||||
@@ -85,18 +88,21 @@ public class TaskbarAutohideSuspendController implements
|
||||
|
||||
boolean isSuspended = isSuspended();
|
||||
mSystemUiProxy.notifyTaskbarAutohideSuspend(isSuspended);
|
||||
mActivity.onTransientAutohideSuspendFlagChanged(isSuspended);
|
||||
mActivity.onTransientAutohideSuspendFlagChanged(isTransientTaskbarStashingSuspended());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true iff taskbar autohide is currently suspended.
|
||||
* Returns true iff taskbar autohide is currently suspended for immersive mode.
|
||||
*/
|
||||
public boolean isSuspended() {
|
||||
private boolean isSuspended() {
|
||||
return mAutohideSuspendFlags != 0;
|
||||
}
|
||||
|
||||
public boolean isSuspendedForTransientTaskbarInOverview() {
|
||||
return (mAutohideSuspendFlags & FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER) != 0;
|
||||
/**
|
||||
* Returns whether Transient Taskbar should avoid auto-stashing.
|
||||
*/
|
||||
public boolean isTransientTaskbarStashingSuspended() {
|
||||
return (mAutohideSuspendFlags & ~FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,6 +121,8 @@ public class TaskbarAutohideSuspendController implements
|
||||
appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_EDU_OPEN, "FLAG_AUTOHIDE_SUSPEND_EDU_OPEN");
|
||||
appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER,
|
||||
"FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER");
|
||||
appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
|
||||
"FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR");
|
||||
return str.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,10 +519,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
|
||||
return;
|
||||
}
|
||||
|
||||
if (stash && mControllers.taskbarAutohideSuspendController.isSuspended()
|
||||
&& !mControllers.taskbarAutohideSuspendController
|
||||
.isSuspendedForTransientTaskbarInOverview()) {
|
||||
// Avoid stashing if autohide is currently suspended.
|
||||
if (stash && mControllers.taskbarAutohideSuspendController
|
||||
.isTransientTaskbarStashingSuspended()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1080,6 +1078,9 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
|
||||
mActivity.getStatsLogManager().logger().log(hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)
|
||||
? LAUNCHER_TRANSIENT_TASKBAR_HIDE
|
||||
: LAUNCHER_TRANSIENT_TASKBAR_SHOW);
|
||||
mControllers.taskbarAutohideSuspendController.updateFlag(
|
||||
TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
|
||||
!hasAnyFlag(FLAG_STASHED_IN_APP_AUTO));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1172,7 +1173,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
|
||||
}
|
||||
|
||||
private void onTaskbarTimeout(Alarm alarm) {
|
||||
if (mControllers.taskbarAutohideSuspendController.isSuspended()) {
|
||||
if (mControllers.taskbarAutohideSuspendController.isTransientTaskbarStashingSuspended()) {
|
||||
return;
|
||||
}
|
||||
updateAndAnimateTransientTaskbarForTimeout();
|
||||
|
||||
@@ -88,6 +88,8 @@ public class BaseDepthController {
|
||||
*/
|
||||
protected boolean mInEarlyWakeUp;
|
||||
|
||||
private boolean mWaitingOnSurfaceValidity;
|
||||
|
||||
public BaseDepthController(Launcher activity) {
|
||||
mLauncher = activity;
|
||||
mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius);
|
||||
@@ -115,6 +117,8 @@ public class BaseDepthController {
|
||||
}
|
||||
}
|
||||
|
||||
protected void onInvalidSurface() { }
|
||||
|
||||
protected void applyDepthAndBlur() {
|
||||
float depth = mDepth;
|
||||
IBinder windowToken = mLauncher.getRootView().getWindowToken();
|
||||
@@ -128,9 +132,15 @@ public class BaseDepthController {
|
||||
if (!BlurUtils.supportsBlursOnWindows()) {
|
||||
return;
|
||||
}
|
||||
if (mSurface == null || !mSurface.isValid()) {
|
||||
if (mSurface == null) {
|
||||
return;
|
||||
}
|
||||
if (!mSurface.isValid()) {
|
||||
mWaitingOnSurfaceValidity = true;
|
||||
onInvalidSurface();
|
||||
return;
|
||||
}
|
||||
mWaitingOnSurfaceValidity = false;
|
||||
boolean hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque();
|
||||
boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg && !mPauseBlurs;
|
||||
|
||||
@@ -174,7 +184,7 @@ public class BaseDepthController {
|
||||
* Sets the specified app target surface to apply the blur to.
|
||||
*/
|
||||
protected void setSurface(SurfaceControl surface) {
|
||||
if (mSurface != surface) {
|
||||
if (mSurface != surface || mWaitingOnSurfaceValidity) {
|
||||
mSurface = surface;
|
||||
applyDepthAndBlur();
|
||||
}
|
||||
|
||||
@@ -204,6 +204,7 @@
|
||||
<!-- File that contains the specs for the workspace.
|
||||
Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
|
||||
<attr name="workspaceSpecsId" format="reference" />
|
||||
|
||||
<!-- By default all categories are enabled -->
|
||||
<attr name="deviceCategory" format="integer">
|
||||
<!-- Enable on phone only -->
|
||||
@@ -251,10 +252,11 @@
|
||||
<attr name="maxAvailableSize" format="dimension" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SpecSize">
|
||||
<declare-styleable name="SizeSpec">
|
||||
<attr name="fixedSize" format="dimension" />
|
||||
<attr name="ofAvailableSpace" format="float" />
|
||||
<attr name="ofRemainderSpace" format="float" />
|
||||
<attr name="matchWorkspace" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ProfileDisplayOption">
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.android.launcher3.responsive
|
||||
|
||||
import android.content.res.TypedArray
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.util.ResourceHelper
|
||||
|
||||
data class SizeSpec(
|
||||
val fixedSize: Float,
|
||||
val ofAvailableSpace: Float,
|
||||
val ofRemainderSpace: Float,
|
||||
val matchWorkspace: Boolean
|
||||
) {
|
||||
|
||||
fun isValid(): Boolean {
|
||||
// All attributes are empty
|
||||
if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f && !matchWorkspace) {
|
||||
Log.e(TAG, "SizeSpec#isValid - all attributes are empty")
|
||||
return false
|
||||
}
|
||||
|
||||
// More than one attribute is filled
|
||||
val attrCount =
|
||||
(if (fixedSize > 0) 1 else 0) +
|
||||
(if (ofAvailableSpace > 0) 1 else 0) +
|
||||
(if (ofRemainderSpace > 0) 1 else 0) +
|
||||
(if (matchWorkspace) 1 else 0)
|
||||
if (attrCount > 1) {
|
||||
Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled")
|
||||
return false
|
||||
}
|
||||
|
||||
// Values should be between 0 and 1
|
||||
if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) {
|
||||
Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1")
|
||||
return false
|
||||
}
|
||||
|
||||
// Invalid fixed size
|
||||
if (fixedSize < 0f) {
|
||||
Log.e(TAG, "SizeSpec#isValid - values should be bigger or equal to zero.")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "WorkspaceSpecs::SizeSpec"
|
||||
private fun getValue(a: TypedArray, index: Int): Float {
|
||||
return when (a.getType(index)) {
|
||||
TypedValue.TYPE_DIMENSION -> a.getDimensionPixelSize(index, 0).toFloat()
|
||||
TypedValue.TYPE_FLOAT -> a.getFloat(index, 0f)
|
||||
else -> 0f
|
||||
}
|
||||
}
|
||||
|
||||
fun create(resourceHelper: ResourceHelper, attrs: AttributeSet): SizeSpec {
|
||||
val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SizeSpec)
|
||||
|
||||
val fixedSize = getValue(styledAttrs, R.styleable.SizeSpec_fixedSize)
|
||||
val ofAvailableSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofAvailableSpace)
|
||||
val ofRemainderSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofRemainderSpace)
|
||||
val matchWorkspace = styledAttrs.getBoolean(R.styleable.SizeSpec_matchWorkspace, false)
|
||||
|
||||
styledAttrs.recycle()
|
||||
|
||||
return SizeSpec(fixedSize, ofAvailableSpace, ofRemainderSpace, matchWorkspace)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,17 +40,14 @@ public class OnboardingPrefs<T extends ActivityContext> {
|
||||
public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";
|
||||
public static final String SEARCH_KEYBOARD_EDU_SEEN = "launcher.search_edu_seen";
|
||||
public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count";
|
||||
public static final String SEARCH_ONBOARDING_COUNT = "launcher.search_onboarding_count";
|
||||
public static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
|
||||
public static final String QSB_SEARCH_ONBOARDING_CARD_DISMISSED = "launcher.qsb_edu_dismiss";
|
||||
public static final String TASKBAR_EDU_TOOLTIP_STEP = "launcher.taskbar_edu_tooltip_step";
|
||||
// When adding a new key, add it here as well, to be able to reset it from Developer Options.
|
||||
public static final Map<String, String[]> ALL_PREF_KEYS = Map.of(
|
||||
"All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT },
|
||||
"Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT,
|
||||
HOTSEAT_LONGPRESS_TIP_SEEN },
|
||||
"Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT,
|
||||
SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED},
|
||||
"Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT},
|
||||
"Taskbar Education", new String[] { TASKBAR_EDU_TOOLTIP_STEP },
|
||||
"All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT}
|
||||
);
|
||||
@@ -62,7 +59,6 @@ public class OnboardingPrefs<T extends ActivityContext> {
|
||||
HOME_BOUNCE_SEEN,
|
||||
HOTSEAT_LONGPRESS_TIP_SEEN,
|
||||
SEARCH_KEYBOARD_EDU_SEEN,
|
||||
QSB_SEARCH_ONBOARDING_CARD_DISMISSED
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface EventBoolKey {}
|
||||
@@ -74,7 +70,6 @@ public class OnboardingPrefs<T extends ActivityContext> {
|
||||
HOME_BOUNCE_COUNT,
|
||||
HOTSEAT_DISCOVERY_TIP_COUNT,
|
||||
SEARCH_SNACKBAR_COUNT,
|
||||
SEARCH_ONBOARDING_COUNT,
|
||||
ALL_APPS_VISITED_COUNT,
|
||||
TASKBAR_EDU_TOOLTIP_STEP,
|
||||
})
|
||||
@@ -88,8 +83,6 @@ public class OnboardingPrefs<T extends ActivityContext> {
|
||||
maxCounts.put(HOME_BOUNCE_COUNT, 3);
|
||||
maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
|
||||
maxCounts.put(SEARCH_SNACKBAR_COUNT, 3);
|
||||
// This is the sum of all onboarding cards. Currently there is only 1 card shown 3 times.
|
||||
maxCounts.put(SEARCH_ONBOARDING_COUNT, 3);
|
||||
maxCounts.put(ALL_APPS_VISITED_COUNT, 20);
|
||||
maxCounts.put(TASKBAR_EDU_TOOLTIP_STEP, 2);
|
||||
MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.launcher3.util;
|
||||
|
||||
import static android.os.VibrationEffect.createPredefined;
|
||||
import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
|
||||
@@ -68,6 +69,9 @@ public class VibratorWrapper {
|
||||
@Nullable
|
||||
private final VibrationEffect mBumpEffect;
|
||||
|
||||
@Nullable
|
||||
private final VibrationEffect mAssistEffect;
|
||||
|
||||
private long mLastDragTime;
|
||||
private final int mThresholdUntilNextDragCallMillis;
|
||||
|
||||
@@ -125,12 +129,25 @@ public class VibratorWrapper {
|
||||
mBumpEffect = null;
|
||||
mThresholdUntilNextDragCallMillis = 0;
|
||||
}
|
||||
|
||||
if (Utilities.ATLEAST_R && mVibrator.areAllPrimitivesSupported(
|
||||
VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
|
||||
VibrationEffect.Composition.PRIMITIVE_TICK)) {
|
||||
// quiet ramp, short pause, then sharp tick
|
||||
mAssistEffect = VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
|
||||
.compose();
|
||||
} else {
|
||||
// fallback for devices without composition support
|
||||
mAssistEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the user swipes to/from all apps. This is meant to be used in between
|
||||
* long animation progresses so that it gives a dragging texture effect. For a better
|
||||
* experience, this should be used in combination with vibrateForDragCommit().
|
||||
* This is called when the user swipes to/from all apps. This is meant to be used in between
|
||||
* long animation progresses so that it gives a dragging texture effect. For a better
|
||||
* experience, this should be used in combination with vibrateForDragCommit().
|
||||
*/
|
||||
public void vibrateForDragTexture() {
|
||||
if (mDragEffect == null) {
|
||||
@@ -145,7 +162,7 @@ public class VibratorWrapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used when user reaches the commit threshold when swiping to/from from all apps.
|
||||
* This is used when user reaches the commit threshold when swiping to/from from all apps.
|
||||
*/
|
||||
public void vibrateForDragCommit() {
|
||||
if (mCommitEffect != null) {
|
||||
@@ -156,9 +173,9 @@ public class VibratorWrapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* The bump haptic is used to be called at the end of a swipe and only if it the gesture is a
|
||||
* FLING going to/from all apps. Client can just call this method elsewhere just for the
|
||||
* effect.
|
||||
* The bump haptic is used to be called at the end of a swipe and only if it the gesture is a
|
||||
* FLING going to/from all apps. Client can just call this method elsewhere just for the
|
||||
* effect.
|
||||
*/
|
||||
public void vibrateForDragBump() {
|
||||
if (mBumpEffect != null) {
|
||||
@@ -166,6 +183,15 @@ public class VibratorWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The assist haptic is used to be called when an assistant is invoked
|
||||
*/
|
||||
public void vibrateForAssist() {
|
||||
if (mAssistEffect != null) {
|
||||
vibrate(mAssistEffect);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be used to cancel a haptic in case where the haptic shouldn't be vibrating. For
|
||||
* example, when no animation is happening but a vibrator happens to be vibrating still. Need
|
||||
@@ -176,6 +202,7 @@ public class VibratorWrapper {
|
||||
// reset dragTexture timestamp to be able to play dragTexture again whenever cancelled
|
||||
mLastDragTime = 0;
|
||||
}
|
||||
|
||||
private boolean isHapticFeedbackEnabled(ContentResolver resolver) {
|
||||
return Settings.System.getInt(resolver, HAPTIC_FEEDBACK_ENABLED, 0) == 1;
|
||||
}
|
||||
|
||||
@@ -774,7 +774,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
super.onCloseComplete();
|
||||
removeCallbacks(mShowEducationTipTask);
|
||||
if (mLatestEducationalTip != null) {
|
||||
mLatestEducationalTip.close(false);
|
||||
mLatestEducationalTip.close(true);
|
||||
}
|
||||
AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL);
|
||||
}
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
package com.android.launcher3.workspace
|
||||
|
||||
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 com.android.launcher3.R
|
||||
import com.android.launcher3.responsive.SizeSpec
|
||||
import com.android.launcher3.util.ResourceHelper
|
||||
import java.io.IOException
|
||||
import kotlin.math.roundToInt
|
||||
@@ -95,16 +94,16 @@ class WorkspaceSpecs(resourceHelper: ResourceHelper) {
|
||||
if (type == XmlPullParser.START_TAG) {
|
||||
when (parser.name) {
|
||||
XmlTags.START_PADDING -> {
|
||||
startPadding = SizeSpec(resourceHelper, attr)
|
||||
startPadding = SizeSpec.create(resourceHelper, attr)
|
||||
}
|
||||
XmlTags.END_PADDING -> {
|
||||
endPadding = SizeSpec(resourceHelper, attr)
|
||||
endPadding = SizeSpec.create(resourceHelper, attr)
|
||||
}
|
||||
XmlTags.GUTTER -> {
|
||||
gutter = SizeSpec(resourceHelper, attr)
|
||||
gutter = SizeSpec.create(resourceHelper, attr)
|
||||
}
|
||||
XmlTags.CELL_SIZE -> {
|
||||
cellSize = SizeSpec(resourceHelper, attr)
|
||||
cellSize = SizeSpec.create(resourceHelper, attr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,61 +269,12 @@ data class WorkspaceSpec(
|
||||
}
|
||||
|
||||
private fun allSpecsAreValid(): Boolean =
|
||||
startPadding.isValid() && endPadding.isValid() && gutter.isValid() && cellSize.isValid()
|
||||
}
|
||||
|
||||
class SizeSpec(resourceHelper: ResourceHelper, attrs: AttributeSet) {
|
||||
val fixedSize: Float
|
||||
val ofAvailableSpace: Float
|
||||
val ofRemainderSpace: Float
|
||||
|
||||
init {
|
||||
val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SpecSize)
|
||||
|
||||
fixedSize = getValue(styledAttrs, R.styleable.SpecSize_fixedSize)
|
||||
ofAvailableSpace = getValue(styledAttrs, R.styleable.SpecSize_ofAvailableSpace)
|
||||
ofRemainderSpace = getValue(styledAttrs, R.styleable.SpecSize_ofRemainderSpace)
|
||||
|
||||
styledAttrs.recycle()
|
||||
}
|
||||
|
||||
private fun getValue(a: TypedArray, index: Int): Float {
|
||||
if (a.getType(index) == TypedValue.TYPE_DIMENSION) {
|
||||
return a.getDimensionPixelSize(index, 0).toFloat()
|
||||
} else if (a.getType(index) == TypedValue.TYPE_FLOAT) {
|
||||
return a.getFloat(index, 0f)
|
||||
}
|
||||
return 0f
|
||||
}
|
||||
|
||||
fun isValid(): Boolean {
|
||||
// All attributes are empty
|
||||
if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f) {
|
||||
Log.e(TAG, "SizeSpec#isValid - all attributes are empty")
|
||||
return false
|
||||
}
|
||||
|
||||
// More than one attribute is filled
|
||||
val attrCount =
|
||||
(if (fixedSize > 0) 1 else 0) +
|
||||
(if (ofAvailableSpace > 0) 1 else 0) +
|
||||
(if (ofRemainderSpace > 0) 1 else 0)
|
||||
if (attrCount > 1) {
|
||||
Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled")
|
||||
return false
|
||||
}
|
||||
|
||||
// Values should be between 0 and 1
|
||||
if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) {
|
||||
Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "SizeSpec(fixedSize=$fixedSize, ofAvailableSpace=$ofAvailableSpace, " +
|
||||
"ofRemainderSpace=$ofRemainderSpace)"
|
||||
}
|
||||
startPadding.isValid() &&
|
||||
endPadding.isValid() &&
|
||||
gutter.isValid() &&
|
||||
cellSize.isValid() &&
|
||||
!startPadding.matchWorkspace &&
|
||||
!endPadding.matchWorkspace &&
|
||||
!gutter.matchWorkspace &&
|
||||
!cellSize.matchWorkspace
|
||||
}
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
<attr name="maxAvailableSize" format="dimension" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SpecSize">
|
||||
<declare-styleable name="SizeSpec">
|
||||
<attr name="fixedSize" format="dimension" />
|
||||
<attr name="ofAvailableSpace" format="float" />
|
||||
<attr name="ofRemainderSpace" format="float" />
|
||||
<attr name="matchWorkspace" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2023 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.
|
||||
-->
|
||||
|
||||
<workspaceSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
|
||||
<workspaceSpec
|
||||
launcher:specType="height"
|
||||
launcher:maxAvailableSize="648dp">
|
||||
<startPadding
|
||||
launcher:ofAvailableSpace="0.0125" />
|
||||
<endPadding
|
||||
launcher:ofAvailableSpace="0.05" />
|
||||
<!-- value in workspace spec using matchWorkspace -->
|
||||
<gutter
|
||||
launcher:matchWorkspace="true" />
|
||||
<cellSize
|
||||
launcher:ofRemainderSpace="0.2" />
|
||||
</workspaceSpec>
|
||||
|
||||
<workspaceSpec
|
||||
launcher:specType="height"
|
||||
launcher:maxAvailableSize="9999dp">
|
||||
<startPadding
|
||||
launcher:ofAvailableSpace="0.0306" />
|
||||
<endPadding
|
||||
launcher:ofAvailableSpace="0.068" />
|
||||
<gutter
|
||||
launcher:fixedSize="16dp" />
|
||||
<cellSize
|
||||
launcher:ofRemainderSpace="0.2" />
|
||||
</workspaceSpec>
|
||||
|
||||
<!-- Width spec is always the same -->
|
||||
<workspaceSpec
|
||||
launcher:specType="width"
|
||||
launcher:maxAvailableSize="9999dp">
|
||||
<startPadding
|
||||
launcher:ofRemainderSpace="0.21436227" />
|
||||
<endPadding
|
||||
launcher:ofRemainderSpace="0.21436227" />
|
||||
<gutter
|
||||
launcher:ofRemainderSpace="0.11425509" />
|
||||
<cellSize
|
||||
launcher:fixedSize="120dp" />
|
||||
</workspaceSpec>
|
||||
</workspaceSpecs>
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.responsive
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.android.launcher3.AbstractDeviceProfileTest
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SizeSpecTest : AbstractDeviceProfileTest() {
|
||||
override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
initializeVarsForPhone(deviceSpecs["phone"]!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun valid_values() {
|
||||
val combinations =
|
||||
listOf(
|
||||
SizeSpec(100f, 0f, 0f, false),
|
||||
SizeSpec(0f, 1f, 0f, false),
|
||||
SizeSpec(0f, 0f, 1f, false),
|
||||
SizeSpec(0f, 0f, 0f, false),
|
||||
SizeSpec(0f, 0f, 0f, true)
|
||||
)
|
||||
|
||||
for (instance in combinations) {
|
||||
assertThat(instance.isValid()).isEqualTo(true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multiple_values_assigned() {
|
||||
val combinations =
|
||||
listOf(
|
||||
SizeSpec(1f, 1f, 0f, false),
|
||||
SizeSpec(1f, 0f, 1f, false),
|
||||
SizeSpec(1f, 0f, 0f, true),
|
||||
SizeSpec(0f, 1f, 1f, false),
|
||||
SizeSpec(0f, 1f, 0f, true),
|
||||
SizeSpec(0f, 0f, 1f, true),
|
||||
SizeSpec(1f, 1f, 1f, true)
|
||||
)
|
||||
|
||||
for (instance in combinations) {
|
||||
assertThat(instance.isValid()).isEqualTo(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun invalid_values() {
|
||||
val combinations =
|
||||
listOf(
|
||||
SizeSpec(0f, 1.1f, 0f, false),
|
||||
SizeSpec(0f, -0.1f, 0f, false),
|
||||
SizeSpec(0f, 0f, 1.1f, false),
|
||||
SizeSpec(0f, 0f, -0.1f, false),
|
||||
SizeSpec(-1f, 0f, 0f, false)
|
||||
)
|
||||
|
||||
for (instance in combinations) {
|
||||
assertThat(instance.isValid()).isEqualTo(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class TestResourceHelper(private val context: Context, private val specsFileId:
|
||||
ResourceHelper(context, specsFileId) {
|
||||
override fun obtainStyledAttributes(attrs: AttributeSet, styleId: IntArray): TypedArray {
|
||||
var clone = styleId.clone()
|
||||
if (styleId == R.styleable.SpecSize) clone = TestR.styleable.SpecSize
|
||||
if (styleId == R.styleable.SizeSpec) clone = TestR.styleable.SizeSpec
|
||||
else if (styleId == R.styleable.WorkspaceSpec) clone = TestR.styleable.WorkspaceSpec
|
||||
return context.obtainStyledAttributes(attrs, clone)
|
||||
}
|
||||
|
||||
@@ -50,16 +50,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
|
||||
"specType=HEIGHT, " +
|
||||
"startPadding=SizeSpec(fixedSize=0.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0), " +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"endPadding=SizeSpec(fixedSize=84.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0), " +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"gutter=SizeSpec(fixedSize=42.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0), " +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"cellSize=SizeSpec(fixedSize=0.0, " +
|
||||
"ofAvailableSpace=0.15808, " +
|
||||
"ofRemainderSpace=0.0)" +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false)" +
|
||||
")"
|
||||
)
|
||||
assertThat(workspaceSpecs.workspaceHeightSpecList[1].toString())
|
||||
@@ -69,16 +73,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
|
||||
"specType=HEIGHT, " +
|
||||
"startPadding=SizeSpec(fixedSize=0.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0), " +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"endPadding=SizeSpec(fixedSize=0.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=1.0), " +
|
||||
"ofRemainderSpace=1.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"gutter=SizeSpec(fixedSize=42.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0), " +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"cellSize=SizeSpec(fixedSize=273.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0)" +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false)" +
|
||||
")"
|
||||
)
|
||||
assertThat(workspaceSpecs.workspaceHeightSpecList[2].toString())
|
||||
@@ -88,16 +96,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
|
||||
"specType=HEIGHT, " +
|
||||
"startPadding=SizeSpec(fixedSize=21.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0), " +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"endPadding=SizeSpec(fixedSize=0.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=1.0), " +
|
||||
"ofRemainderSpace=1.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"gutter=SizeSpec(fixedSize=42.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0), " +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"cellSize=SizeSpec(fixedSize=273.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0)" +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false)" +
|
||||
")"
|
||||
)
|
||||
assertThat(workspaceSpecs.workspaceWidthSpecList.size).isEqualTo(1)
|
||||
@@ -108,16 +120,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
|
||||
"specType=WIDTH, " +
|
||||
"startPadding=SizeSpec(fixedSize=58.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0), " +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"endPadding=SizeSpec(fixedSize=58.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0), " +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"gutter=SizeSpec(fixedSize=42.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.0), " +
|
||||
"ofRemainderSpace=0.0, " +
|
||||
"matchWorkspace=false), " +
|
||||
"cellSize=SizeSpec(fixedSize=0.0, " +
|
||||
"ofAvailableSpace=0.0, " +
|
||||
"ofRemainderSpace=0.25)" +
|
||||
"ofRemainderSpace=0.25, " +
|
||||
"matchWorkspace=false)" +
|
||||
")"
|
||||
)
|
||||
}
|
||||
@@ -136,4 +152,9 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
|
||||
fun parseInvalidFile_valueBiggerThan1_throwsError() {
|
||||
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_3))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun parseInvalidFile_matchWorkspace_true_throwsError() {
|
||||
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_4))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user