Merge "Create a better implementation for permission GIDs." into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 54b852c..ed05047 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1496,6 +1496,7 @@
field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261
field public static final int sharedUserMaxSdkVersion = 16844365; // 0x101064d
field public static final int shell = 16844180; // 0x1010594
+ field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int shiftDrawingOffsetForStartOverhang;
field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
field public static final int shortcutId = 16844072; // 0x1010528
field public static final int shortcutLongLabel = 16844074; // 0x101052a
@@ -47547,6 +47548,7 @@
method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
method @NonNull public android.text.DynamicLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float);
method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.DynamicLayout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setShiftDrawingOffsetForStartOverhang(boolean);
method @NonNull public android.text.DynamicLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean);
method @NonNull public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean);
@@ -47750,6 +47752,7 @@
method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @Nullable public final int[] getRightIndents();
method public float getSecondaryHorizontal(int);
method public void getSelectionPath(int, int, android.graphics.Path);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public boolean getShiftDrawingOffsetForStartOverhang();
method public final float getSpacingAdd();
method public final float getSpacingMultiplier();
method @NonNull public final CharSequence getText();
@@ -47806,6 +47809,7 @@
method @NonNull public android.text.Layout.Builder setMaxLines(@IntRange(from=1) int);
method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.Layout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics);
method @NonNull public android.text.Layout.Builder setRightIndents(@Nullable int[]);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.Layout.Builder setShiftDrawingOffsetForStartOverhang(boolean);
method @NonNull public android.text.Layout.Builder setTextDirectionHeuristic(@NonNull android.text.TextDirectionHeuristic);
method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean);
}
@@ -48077,6 +48081,7 @@
method @NonNull public android.text.StaticLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float);
method @NonNull public android.text.StaticLayout.Builder setMaxLines(@IntRange(from=0) int);
method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.StaticLayout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setShiftDrawingOffsetForStartOverhang(boolean);
method public android.text.StaticLayout.Builder setText(CharSequence);
method @NonNull public android.text.StaticLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean);
@@ -50586,7 +50591,6 @@
method public default void removeOnBufferTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnBufferTransformHintChangedListener);
method public default void setChildBoundingInsets(@NonNull android.graphics.Rect);
method public default void setTouchableRegion(@Nullable android.graphics.Region);
- method @FlaggedApi("com.android.window.flags.transfer_gesture_to_embedded") public default boolean transferHostTouchGestureToEmbedded(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
}
@UiThread public static interface AttachedSurfaceControl.OnBufferTransformHintChangedListener {
@@ -52304,6 +52308,7 @@
public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
ctor public SurfaceControlViewHost.SurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
method public int describeContents();
+ method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @Nullable public android.window.InputTransferToken getInputTransferToken();
method @NonNull public android.view.SurfaceControl getSurfaceControl();
method public void notifyConfigurationChanged(@NonNull android.content.res.Configuration);
method public void notifyDetachedFromWindow();
@@ -53264,7 +53269,7 @@
field protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
field protected static final int[] PRESSED_STATE_SET;
field protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET;
- field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0.0f;
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = (0.0f/0.0f);
field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -4.0f;
field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -2.0f;
field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_NORMAL = -3.0f;
@@ -54446,6 +54451,7 @@
method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer);
method @FlaggedApi("com.android.window.flags.screen_recording_callbacks") @RequiresPermission(android.Manifest.permission.DETECT_SCREEN_RECORDING) public default void removeScreenRecordingCallback(@NonNull java.util.function.Consumer<java.lang.Integer>);
method public void removeViewImmediate(android.view.View);
+ method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default boolean transferTouchGesture(@NonNull android.window.InputTransferToken, @NonNull android.window.InputTransferToken);
method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void unregisterSurfaceControlInputReceiver(@NonNull android.view.SurfaceControl);
method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void unregisterTrustedPresentationListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
@@ -60893,6 +60899,7 @@
method public float getShadowDx();
method public float getShadowDy();
method public float getShadowRadius();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public boolean getShiftDrawingOffsetForStartOverhang();
method public final boolean getShowSoftInputOnFocus();
method public CharSequence getText();
method @NonNull public android.view.textclassifier.TextClassifier getTextClassifier();
@@ -61029,6 +61036,7 @@
method public void setSearchResultHighlights(@Nullable int...);
method public void setSelectAllOnFocus(boolean);
method public void setShadowLayer(float, float, float, int);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public void setShiftDrawingOffsetForStartOverhang(boolean);
method public final void setShowSoftInputOnFocus(boolean);
method public void setSingleLine();
method public void setSingleLine(boolean);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 49c8a7c..2a67353 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2554,6 +2554,15 @@
public static final int INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST = -130;
/**
+ * Installation failed return code: if the system failed to install the package that
+ * {@link android.R.attr#multiArch} is true in its manifest because its packaged
+ * native code did not match all of the natively ABIs supported by the system.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS = -131;
+
+ /**
* App minimum aspect ratio set by the user which will override app-defined aspect ratio.
*
* @hide
@@ -10452,6 +10461,8 @@
case INSTALL_FAILED_SESSION_INVALID: return "INSTALL_FAILED_SESSION_INVALID";
case INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST:
return "INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST";
+ case INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS:
+ return "INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS";
default: return Integer.toString(status);
}
}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 2028c40..9db8aa1 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -273,7 +273,7 @@
outerwidth /* ellipsizedWidth */, null /* ellipsize */, 1 /* maxLines */,
BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */,
null /* rightIndents */, JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false,
- null);
+ false /* shiftDrawingOffsetForStartOverhang */, null);
mEllipsizedWidth = outerwidth;
mEllipsizedStart = 0;
@@ -346,7 +346,8 @@
ellipsizedWidth, ellipsize, 1 /* maxLines */,
BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */,
null /* rightIndents */, JUSTIFICATION_MODE_NONE,
- LineBreakConfig.NONE, metrics, false /* useBoundsForWidth */, null);
+ LineBreakConfig.NONE, metrics, false /* useBoundsForWidth */,
+ false /* shiftDrawingOffsetForStartOverhang */, null);
}
/** @hide */
@@ -363,12 +364,14 @@
TextUtils.TruncateAt ellipsize,
Metrics metrics,
boolean useBoundsForWidth,
+ boolean shiftDrawingOffsetForStartOverhang,
@Nullable Paint.FontMetrics minimumFontMetrics) {
this(text, paint, width, align, TextDirectionHeuristics.LTR,
spacingMult, spacingAdd, includePad, fallbackLineSpacing, ellipsizedWidth,
ellipsize, 1 /* maxLines */, Layout.BREAK_STRATEGY_SIMPLE,
Layout.HYPHENATION_FREQUENCY_NONE, null, null, Layout.JUSTIFICATION_MODE_NONE,
- LineBreakConfig.NONE, metrics, useBoundsForWidth, minimumFontMetrics);
+ LineBreakConfig.NONE, metrics, useBoundsForWidth,
+ shiftDrawingOffsetForStartOverhang, minimumFontMetrics);
}
/* package */ BoringLayout(
@@ -392,12 +395,14 @@
LineBreakConfig lineBreakConfig,
Metrics metrics,
boolean useBoundsForWidth,
+ boolean shiftDrawingOffsetForStartOverhang,
@Nullable Paint.FontMetrics minimumFontMetrics) {
super(text, paint, width, align, textDir, spacingMult, spacingAdd, includePad,
fallbackLineSpacing, ellipsizedWidth, ellipsize, maxLines, breakStrategy,
hyphenationFrequency, leftIndents, rightIndents, justificationMode,
- lineBreakConfig, useBoundsForWidth, minimumFontMetrics);
+ lineBreakConfig, useBoundsForWidth, shiftDrawingOffsetForStartOverhang,
+ minimumFontMetrics);
boolean trust;
@@ -712,7 +717,7 @@
int cursorOffset) {
if (mDirect != null && highlight == null) {
float leftShift = 0;
- if (getUseBoundsForWidth()) {
+ if (getUseBoundsForWidth() && getShiftDrawingOffsetForStartOverhang()) {
RectF drawingRect = computeDrawingBoundingBox();
if (drawingRect.left < 0) {
leftShift = -drawingRect.left;
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 9286049..cce4f7b 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -25,6 +25,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -317,6 +318,35 @@
}
/**
+ * Set true for shifting the drawing x offset for showing overhang at the start position.
+ *
+ * This flag is ignored if the {@link #getUseBoundsForWidth()} is false.
+ *
+ * If this value is false, the Layout draws text from the zero even if there is a glyph
+ * stroke in a region where the x coordinate is negative.
+ *
+ * If this value is true, the Layout draws text with shifting the x coordinate of the
+ * drawing bounding box.
+ *
+ * This value is false by default.
+ *
+ * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for
+ * showing the stroke that is in the region where
+ * the x coordinate is negative.
+ * @see #setUseBoundsForWidth(boolean)
+ * @see #getUseBoundsForWidth()
+ */
+ @NonNull
+ // The corresponding getter is getShiftDrawingOffsetForStartOverhang()
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public Builder setShiftDrawingOffsetForStartOverhang(
+ boolean shiftDrawingOffsetForStartOverhang) {
+ mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
+ return this;
+ }
+
+ /**
* Set the minimum font metrics used for line spacing.
*
* <p>
@@ -386,6 +416,7 @@
private int mEllipsizedWidth;
private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
private @Nullable Paint.FontMetrics mMinimumFontMetrics;
private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
@@ -462,7 +493,8 @@
false /* fallbackLineSpacing */, ellipsizedWidth, ellipsize,
Integer.MAX_VALUE /* maxLines */, breakStrategy, hyphenationFrequency,
null /* leftIndents */, null /* rightIndents */, justificationMode,
- lineBreakConfig, false /* useBoundsForWidth */, null /* minimumFontMetrics */);
+ lineBreakConfig, false /* useBoundsForWidth */, false,
+ null /* minimumFontMetrics */);
final Builder b = Builder.obtain(base, paint, width)
.setAlignment(align)
@@ -488,7 +520,8 @@
b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
Integer.MAX_VALUE /* maxLines */, b.mBreakStrategy, b.mHyphenationFrequency,
null /* leftIndents */, null /* rightIndents */, b.mJustificationMode,
- b.mLineBreakConfig, b.mUseBoundsForWidth, b.mMinimumFontMetrics);
+ b.mLineBreakConfig, b.mUseBoundsForWidth, b.mShiftDrawingOffsetForStartOverhang,
+ b.mMinimumFontMetrics);
mDisplay = b.mDisplay;
mIncludePad = b.mIncludePad;
@@ -516,6 +549,7 @@
mBase = b.mBase;
mFallbackLineSpacing = b.mFallbackLineSpacing;
mUseBoundsForWidth = b.mUseBoundsForWidth;
+ mShiftDrawingOffsetForStartOverhang = b.mShiftDrawingOffsetForStartOverhang;
mMinimumFontMetrics = b.mMinimumFontMetrics;
if (b.mEllipsize != null) {
mInts = new PackedIntVector(COLUMNS_ELLIPSIZE);
@@ -713,6 +747,7 @@
.setAddLastLineLineSpacing(!islast)
.setIncludePad(false)
.setUseBoundsForWidth(mUseBoundsForWidth)
+ .setShiftDrawingOffsetForStartOverhang(mShiftDrawingOffsetForStartOverhang)
.setMinimumFontMetrics(mMinimumFontMetrics)
.setCalculateBounds(true);
@@ -1392,6 +1427,7 @@
private Rect mTempRect = new Rect();
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
@Nullable Paint.FontMetrics mMinimumFontMetrics;
@UnsupportedAppUsage
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index e5d199a..8e52af3 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -44,6 +44,7 @@
import android.text.style.ParagraphStyle;
import android.text.style.ReplacementSpan;
import android.text.style.TabStopSpan;
+import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -299,7 +300,7 @@
this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
spacingMult, spacingAdd, false, false, 0, null, Integer.MAX_VALUE,
BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null, null,
- JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false, null);
+ JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false, false, null);
}
/**
@@ -349,6 +350,7 @@
int justificationMode,
LineBreakConfig lineBreakConfig,
boolean useBoundsForWidth,
+ boolean shiftDrawingOffsetForStartOverhang,
Paint.FontMetrics minimumFontMetrics
) {
@@ -384,6 +386,7 @@
mJustificationMode = justificationMode;
mLineBreakConfig = lineBreakConfig;
mUseBoundsForWidth = useBoundsForWidth;
+ mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
mMinimumFontMetrics = minimumFontMetrics;
}
@@ -465,7 +468,7 @@
@Nullable Paint selectionPaint,
int cursorOffsetVertical) {
float leftShift = 0;
- if (mUseBoundsForWidth) {
+ if (mUseBoundsForWidth && mShiftDrawingOffsetForStartOverhang) {
RectF drawingRect = computeDrawingBoundingBox();
if (drawingRect.left < 0) {
leftShift = -drawingRect.left;
@@ -3414,6 +3417,7 @@
private int mJustificationMode;
private LineBreakConfig mLineBreakConfig;
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
private @Nullable Paint.FontMetrics mMinimumFontMetrics;
private TextLine.LineInfo mLineInfo = null;
@@ -3873,6 +3877,35 @@
}
/**
+ * Set true for shifting the drawing x offset for showing overhang at the start position.
+ *
+ * This flag is ignored if the {@link #getUseBoundsForWidth()} is false.
+ *
+ * If this value is false, the Layout draws text from the zero even if there is a glyph
+ * stroke in a region where the x coordinate is negative.
+ *
+ * If this value is true, the Layout draws text with shifting the x coordinate of the
+ * drawing bounding box.
+ *
+ * This value is false by default.
+ *
+ * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for
+ * showing the stroke that is in the region where
+ * the x coordinate is negative.
+ * @see #setUseBoundsForWidth(boolean)
+ * @see #getUseBoundsForWidth()
+ */
+ @NonNull
+ // The corresponding getter is getShiftDrawingOffsetForStartOverhang()
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public Builder setShiftDrawingOffsetForStartOverhang(
+ boolean shiftDrawingOffsetForStartOverhang) {
+ mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
+ return this;
+ }
+
+ /**
* Set the minimum font metrics used for line spacing.
*
* <p>
@@ -3948,6 +3981,7 @@
.setJustificationMode(mJustificationMode)
.setLineBreakConfig(mLineBreakConfig)
.setUseBoundsForWidth(mUseBoundsForWidth)
+ .setShiftDrawingOffsetForStartOverhang(mShiftDrawingOffsetForStartOverhang)
.build();
} else {
return new BoringLayout(
@@ -3955,7 +3989,7 @@
mIncludePad, mFallbackLineSpacing, mEllipsizedWidth, mEllipsize, mMaxLines,
mBreakStrategy, mHyphenationFrequency, mLeftIndents, mRightIndents,
mJustificationMode, mLineBreakConfig, metrics, mUseBoundsForWidth,
- mMinimumFontMetrics);
+ mShiftDrawingOffsetForStartOverhang, mMinimumFontMetrics);
}
}
@@ -3980,6 +4014,7 @@
private int mJustificationMode = JUSTIFICATION_MODE_NONE;
private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
private Paint.FontMetrics mMinimumFontMetrics;
}
@@ -4294,6 +4329,20 @@
}
/**
+ * Returns true if shifting drawing offset for start overhang.
+ *
+ * @return True if shifting drawing offset for start overhang.
+ * @see android.widget.TextView#setShiftDrawingOffsetForStartOverhang(boolean)
+ * @see TextView#getShiftDrawingOffsetForStartOverhang()
+ * @see StaticLayout.Builder#setShiftDrawingOffsetForStartOverhang(boolean)
+ * @see DynamicLayout.Builder#setShiftDrawingOffsetForStartOverhang(boolean)
+ */
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public boolean getShiftDrawingOffsetForStartOverhang() {
+ return mShiftDrawingOffsetForStartOverhang;
+ }
+
+ /**
* Get the minimum font metrics used for line spacing.
*
* @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics)
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 5986238..3dd3a9e 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -24,6 +24,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
import android.graphics.RectF;
@@ -454,6 +455,35 @@
}
/**
+ * Set true for shifting the drawing x offset for showing overhang at the start position.
+ *
+ * This flag is ignored if the {@link #getUseBoundsForWidth()} is false.
+ *
+ * If this value is false, the Layout draws text from the zero even if there is a glyph
+ * stroke in a region where the x coordinate is negative.
+ *
+ * If this value is true, the Layout draws text with shifting the x coordinate of the
+ * drawing bounding box.
+ *
+ * This value is false by default.
+ *
+ * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for
+ * showing the stroke that is in the region where
+ * the x coordinate is negative.
+ * @see #setUseBoundsForWidth(boolean)
+ * @see #getUseBoundsForWidth()
+ */
+ @NonNull
+ // The corresponding getter is getShiftDrawingOffsetForStartOverhang()
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public Builder setShiftDrawingOffsetForStartOverhang(
+ boolean shiftDrawingOffsetForStartOverhang) {
+ mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
+ return this;
+ }
+
+ /**
* Internal API that tells underlying line breaker that calculating bounding boxes even if
* the line break is performed with advances. This is useful for DynamicLayout internal
* implementation because it uses bounding box as well as advances.
@@ -566,6 +596,7 @@
private boolean mAddLastLineLineSpacing;
private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
private boolean mCalculateBounds;
@Nullable private Paint.FontMetrics mMinimumFontMetrics;
@@ -599,6 +630,7 @@
JUSTIFICATION_MODE_NONE,
null, // lineBreakConfig,
false, // useBoundsForWidth
+ false, // shiftDrawingOffsetForStartOverhang
null // minimumFontMetrics
);
@@ -677,7 +709,7 @@
b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
b.mMaxLines, b.mBreakStrategy, b.mHyphenationFrequency, b.mLeftIndents,
b.mRightIndents, b.mJustificationMode, b.mLineBreakConfig, b.mUseBoundsForWidth,
- b.mMinimumFontMetrics);
+ b.mShiftDrawingOffsetForStartOverhang, b.mMinimumFontMetrics);
mColumns = columnSize;
if (b.mEllipsize != null) {
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index 9413f5c..5ec41591 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -202,38 +202,4 @@
throw new UnsupportedOperationException("The getInputTransferToken needs to be "
+ "implemented before making this call.");
}
-
- /**
- * Transfer the currently in progress touch gesture from the host to the requested
- * {@link SurfaceControlViewHost.SurfacePackage}. This requires that the
- * SurfaceControlViewHost was created with the current host's inputToken.
- * <p>
- * When the touch is transferred, the window currently receiving touch gets an ACTION_CANCEL
- * and does not receive any further input events for this gesture.
- * <p>
- * The transferred-to window receives an ACTION_DOWN event and then the remainder of the
- * input events for this gesture. It does not receive any of the previous events of this gesture
- * that the originating window received.
- * <p>
- * The "transferTouch" API only works for the current gesture. When a new gesture arrives,
- * input dispatcher will do a new round of hit testing. So, if the "host" window is still the
- * first thing that's being touched, then it will receive the new gesture again. It will
- * again be up to the host to transfer this new gesture to the embedded.
- * <p>
- * Once the transferred-to window receives the gesture, it can choose to give up this gesture
- * and send it to another window that it's linked to (it can't be an arbitrary window for
- * security reasons) using the same transferTouch API. Only the window currently receiving
- * touch is allowed to transfer the gesture.
- *
- * @param surfacePackage The SurfacePackage to transfer the gesture to.
- * @return Whether the touch stream was transferred.
- * @see SurfaceControlViewHost#transferTouchGestureToHost() for the reverse to transfer touch
- * gesture from the embedded to the host.
- */
- @FlaggedApi(Flags.FLAG_TRANSFER_GESTURE_TO_EMBEDDED)
- default boolean transferHostTouchGestureToEmbedded(
- @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
- throw new UnsupportedOperationException(
- "transferHostTouchGestureToEmbedded is unimplemented");
- }
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 29cc859..c475f6b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -1098,4 +1098,7 @@
* (ie. not handled by any window which can handle the drag).
*/
void setUnhandledDragListener(IUnhandledDragListener listener);
+
+ boolean transferTouchGesture(in InputTransferToken transferFromToken,
+ in InputTransferToken transferToToken);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 55e49f8..d68a47c 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -370,11 +370,6 @@
*/
boolean cancelDraw(IWindow window);
- boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow);
-
- boolean transferHostTouchGestureToEmbedded(IWindow hostWindow,
- in InputTransferToken transferTouchToken);
-
/**
* Moves the focus to the adjacent window if there is one in the given direction. This can only
* move the focus to the window in the same leaf task.
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 1dd9cbb..06a923a 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -293,12 +293,12 @@
/**
* Gets an {@link InputTransferToken} which can be used to request focus on the embedded
* surface or to transfer touch gesture to the embedded surface.
- * @return the InputTransferToken associated with {@link SurfacePackage}
- * @see AttachedSurfaceControl#transferHostTouchGestureToEmbedded(SurfacePackage)
- *
- * @hide
+ * @return the InputTransferToken associated with {@link SurfacePackage} or {@code null} if
+ * the embedded hasn't set up its view or doesn't have input.
+ * @see WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken)
*/
@Nullable
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
public InputTransferToken getInputTransferToken() {
return mInputTransferToken;
}
@@ -577,9 +577,9 @@
}
/**
- * Transfer the currently in progress touch gesture to the parent
- * (if any) of this SurfaceControlViewHost. This requires that the
- * SurfaceControlViewHost was created with an associated hostInputToken.
+ * Transfer the currently in progress touch gesture to the parent (if any) of this
+ * SurfaceControlViewHost. This requires that the SurfaceControlViewHost was created with an
+ * associated host {@link InputTransferToken}.
*
* @return Whether the touch stream was transferred.
*/
@@ -587,13 +587,14 @@
if (mViewRoot == null) {
return false;
}
-
- final IWindowSession realWm = WindowManagerGlobal.getWindowSession();
- try {
- return realWm.transferEmbeddedTouchFocusToHost(mViewRoot.mWindow);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ final WindowManager wm =
+ (WindowManager) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE);
+ InputTransferToken embeddedToken = getInputTransferToken();
+ InputTransferToken hostToken = mWm.mHostInputTransferToken;
+ if (embeddedToken == null || hostToken == null) {
+ Log.w(TAG, "Failed to transferTouchGestureToHost. Host or embedded token is null");
+ return false;
}
- return false;
+ return wm.transferTouchGesture(getInputTransferToken(), mWm.mHostInputTransferToken);
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 25ade62..73b6ed6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5638,7 +5638,7 @@
private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
@FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
- public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0;
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = Float.NaN;
@FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
public static final float REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE = -1;
@FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
@@ -33457,7 +33457,6 @@
if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) {
return FRAME_RATE_CATEGORY_NORMAL;
}
-
return mLastFrameRateCategory;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c5cd70f..9474a69 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -27,6 +27,8 @@
import static android.view.InsetsSource.ID_IME;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
+import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -1031,6 +1033,15 @@
private static final int FRAME_RATE_SETTING_REEVALUATE_TIME = 100;
/*
+ * The variables below are used to update frame rate category
+ */
+ private static final int FRAME_RATE_CATEGORY_COUNT = 5;
+ private int mFrameRateCategoryHighCount = 0;
+ private int mFrameRateCategoryHighHintCount = 0;
+ private int mFrameRateCategoryNormalCount = 0;
+ private int mFrameRateCategoryLowCount = 0;
+
+ /*
* the variables below are used to determine whther a dVRR feature should be enabled
*/
@@ -4084,6 +4095,12 @@
// when the values are applicable.
setPreferredFrameRate(mPreferredFrameRate);
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0
+ ? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount;
+ mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0
+ ? mFrameRateCategoryNormalCount - 1 : mFrameRateCategoryNormalCount;
+ mFrameRateCategoryLowCount = mFrameRateCategoryLowCount > 0
+ ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount;
mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
mPreferredFrameRate = -1;
}
@@ -12348,7 +12365,25 @@
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
public void votePreferredFrameRateCategory(int frameRateCategory) {
- mPreferredFrameRateCategory = Math.max(mPreferredFrameRateCategory, frameRateCategory);
+ if (frameRateCategory == FRAME_RATE_CATEGORY_HIGH) {
+ mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
+ } else if (frameRateCategory == FRAME_RATE_CATEGORY_HIGH_HINT) {
+ mFrameRateCategoryHighHintCount = FRAME_RATE_CATEGORY_COUNT;
+ } else if (frameRateCategory == FRAME_RATE_CATEGORY_NORMAL) {
+ mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT;
+ } else if (frameRateCategory == FRAME_RATE_CATEGORY_LOW) {
+ mFrameRateCategoryLowCount = FRAME_RATE_CATEGORY_COUNT;
+ }
+
+ if (mFrameRateCategoryHighCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
+ } else if (mFrameRateCategoryHighHintCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT;
+ } else if (mFrameRateCategoryNormalCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL;
+ } else if (mFrameRateCategoryLowCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW;
+ }
mHasInvalidation = true;
}
@@ -12427,19 +12462,6 @@
boostTimeOut);
}
- @Override
- public boolean transferHostTouchGestureToEmbedded(
- @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
- final IWindowSession realWm = WindowManagerGlobal.getWindowSession();
- try {
- return realWm.transferHostTouchGestureToEmbedded(mWindow,
- surfacePackage.getInputTransferToken());
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- return false;
- }
-
/**
* Set the default back key callback for windowless window, to forward the back key event
* to host app.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0302a0d..bc9b4fe 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -6196,6 +6196,54 @@
}
/**
+ * Transfer the currently in progress touch gesture from the transferFromToken to the
+ * transferToToken.
+ * <p><br>
+ * This requires that the fromToken and toToken are associated with each other. The association
+ * can be done by creating a {@link SurfaceControlViewHost} and passing the host's
+ * {@link InputTransferToken} for
+ * {@link SurfaceControlViewHost#SurfaceControlViewHost(Context, Display, InputTransferToken)}.
+ * <p>
+ * The host is likely to be an {@link AttachedSurfaceControl} so the host token can be
+ * retrieved via {@link AttachedSurfaceControl#getInputTransferToken()}.
+ * <p><br>
+ * Only the window currently receiving touch is allowed to transfer the gesture so if the caller
+ * attempts to transfer touch gesture from a token that doesn't have touch, it will fail the
+ * transfer.
+ * <p><br>
+ * When the host wants to transfer touch gesture to the embedded, it can retrieve the embedded
+ * token via {@link SurfaceControlViewHost.SurfacePackage#getInputTransferToken()} and pass its
+ * own token as the transferFromToken.
+ * <p>
+ * When the embedded wants to transfer touch gesture to the host, it can pass in its own token
+ * as the transferFromToken and use the associated host's {@link InputTransferToken} as the
+ * transferToToken
+ * <p><br>
+ * When the touch is transferred, the window currently receiving touch gets an ACTION_CANCEL
+ * and does not receive any further input events for this gesture.
+ * <p>
+ * The transferred-to window receives an ACTION_DOWN event and then the remainder of the input
+ * events for this gesture. It does not receive any of the previous events of this gesture that
+ * the originating window received.
+ * <p>
+ * The transferTouchGesture API only works for the current gesture. When a new gesture arrives,
+ * input dispatcher will do a new round of hit testing. So, if the host window is still the
+ * first thing that's being touched, then it will receive the new gesture again. It will again
+ * be up to the host to transfer this new gesture to the embedded.
+ *
+ * @param transferFromToken the InputTransferToken for the currently active gesture
+ * @param transferToToken the InputTransferToken to transfer the gesture to.
+ * @return Whether the touch stream was transferred.
+ * @see android.view.SurfaceControlViewHost.SurfacePackage#getInputTransferToken()
+ * @see AttachedSurfaceControl#getInputTransferToken()
+ */
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
+ default boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
+ @NonNull InputTransferToken transferToToken) {
+ throw new UnsupportedOperationException("transferTouchGesture is not implemented");
+ }
+
+ /**
* @hide
*/
default @NonNull IBinder getDefaultToken() {
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 1428963..2c98d9a 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -930,6 +930,17 @@
return surfaceControlInputReceiverInfo.mClientToken;
}
+ boolean transferTouchGesture(InputTransferToken transferFromToken,
+ InputTransferToken transferToToken) {
+ try {
+ return getWindowManagerService().transferTouchGesture(transferFromToken,
+ transferToToken);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ return false;
+ }
+
private final class TrustedPresentationListener extends
ITrustedPresentationListener.Stub {
private static int sId = 0;
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 1e3d062..44e2493 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -563,6 +563,14 @@
}
@Override
+ public boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
+ @NonNull InputTransferToken transferToToken) {
+ Objects.requireNonNull(transferFromToken);
+ Objects.requireNonNull(transferToToken);
+ return mGlobal.transferTouchGesture(transferFromToken, transferToToken);
+ }
+
+ @Override
public @ScreenRecordingState int addScreenRecordingCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<@ScreenRecordingState Integer> callback) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 3f1ae51..2b2c507 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -651,21 +651,6 @@
}
@Override
- public boolean transferEmbeddedTouchFocusToHost(IWindow window) {
- Log.e(TAG, "Received request to transferEmbeddedTouch focus on WindowlessWindowManager" +
- " we shouldn't get here!");
- return false;
- }
-
- @Override
- public boolean transferHostTouchGestureToEmbedded(IWindow hostWindow,
- InputTransferToken embeddedInputToken) {
- Log.e(TAG, "Received request to transferHostTouchGestureToEmbedded on"
- + " WindowlessWindowManager. We shouldn't get here!");
- return false;
- }
-
- @Override
public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
Log.e(TAG, "Received request to moveFocusToAdjacentWindow on"
+ " WindowlessWindowManager. We shouldn't get here!");
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 57e4e6a..9847cb1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -181,6 +181,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewHierarchyEncoder;
import android.view.ViewParent;
@@ -866,6 +867,7 @@
private final boolean mUseTextPaddingForUiTranslation;
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
@Nullable private Paint.FontMetrics mMinimumFontMetrics;
@Nullable private Paint.FontMetrics mLocalePreferredFontMetrics;
private boolean mUseLocalePreferredLineHeightForMinimum;
@@ -1621,6 +1623,10 @@
hasUseBoundForWidthValue = true;
break;
case com.android.internal.R.styleable
+ .TextView_shiftDrawingOffsetForStartOverhang:
+ mShiftDrawingOffsetForStartOverhang = a.getBoolean(attr, false);
+ break;
+ case com.android.internal.R.styleable
.TextView_useLocalePreferredLineHeightForMinimum:
mUseLocalePreferredLineHeightForMinimum = a.getBoolean(attr, false);
break;
@@ -4922,6 +4928,8 @@
* @param useBoundsForWidth true for using bounding box for width. false for using advances for
* width.
* @see #getUseBoundsForWidth()
+ * @see #setShiftDrawingOffsetForStartOverhang(boolean)
+ * @see #getShiftDrawingOffsetForStartOverhang()
*/
@FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public void setUseBoundsForWidth(boolean useBoundsForWidth) {
@@ -4939,6 +4947,8 @@
* Returns true if using bounding box as a width, false for using advance as a width.
*
* @see #setUseBoundsForWidth(boolean)
+ * @see #setShiftDrawingOffsetForStartOverhang(boolean)
+ * @see #getShiftDrawingOffsetForStartOverhang()
* @return True if using bounding box for width, false if using advance for width.
*/
@FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
@@ -4947,6 +4957,53 @@
}
/**
+ * Set true for shifting the drawing x offset for showing overhang at the start position.
+ *
+ * This flag is ignored if the {@link #getUseBoundsForWidth()} is false.
+ *
+ * If this value is false, the TextView draws text from the zero even if there is a glyph stroke
+ * in a region where the x coordinate is negative. TextView clips the stroke in the region where
+ * the X coordinate is negative unless the parents has {@link ViewGroup#getClipChildren()} to
+ * true. This is useful for aligning multiple TextViews vertically.
+ *
+ * If this value is true, the TextView draws text with shifting the x coordinate of the drawing
+ * bounding box. This prevents the clipping even if the parents doesn't have
+ * {@link ViewGroup#getClipChildren()} to true.
+ *
+ * This value is false by default.
+ *
+ * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for showing
+ * the stroke that is in the region whre the x
+ * coorinate is negative.
+ * @see #setUseBoundsForWidth(boolean)
+ * @see #getUseBoundsForWidth()
+ */
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public void setShiftDrawingOffsetForStartOverhang(boolean shiftDrawingOffsetForStartOverhang) {
+ if (mShiftDrawingOffsetForStartOverhang != shiftDrawingOffsetForStartOverhang) {
+ mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
+ }
+ }
+
+ /**
+ * Returns true if shifting the drawing x offset for start overhang.
+ *
+ * @see #setShiftDrawingOffsetForStartOverhang(boolean)
+ * @see #setUseBoundsForWidth(boolean)
+ * @see #getUseBoundsForWidth()
+ * @return True if shifting the drawing x offset for start overhang.
+ */
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public boolean getShiftDrawingOffsetForStartOverhang() {
+ return mShiftDrawingOffsetForStartOverhang;
+ }
+
+ /**
* Set the minimum font metrics used for line spacing.
*
* <p>
@@ -11001,6 +11058,7 @@
null,
boring,
mUseBoundsForWidth,
+ mShiftDrawingOffsetForStartOverhang,
getResolvedMinimumFontMetrics());
}
@@ -11028,6 +11086,7 @@
effectiveEllipsize,
boring,
mUseBoundsForWidth,
+ mShiftDrawingOffsetForStartOverhang,
getResolvedMinimumFontMetrics());
}
}
diff --git a/core/java/android/window/InputTransferToken.java b/core/java/android/window/InputTransferToken.java
index bed0e0e..f1b37bf 100644
--- a/core/java/android/window/InputTransferToken.java
+++ b/core/java/android/window/InputTransferToken.java
@@ -31,6 +31,11 @@
/**
* A token that can be used to request focus on or to transfer touch gesture to a
* {@link SurfaceControlViewHost} or {@link android.view.SurfaceControl} that has an input channel.
+ * <p>
+ * For {@link SurfaceControlViewHost}, the token can be retrieved via
+ * {@link SurfaceControlViewHost.SurfacePackage#getInputTransferToken()}
+ *
+ * @see android.view.WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken)
*/
@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
public final class InputTransferToken implements Parcelable {
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 3ffa274..8b3bd97 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -20,13 +20,6 @@
flag {
namespace: "window_surfaces"
- name: "transfer_gesture_to_embedded"
- description: "Enable public API for Window Surfaces"
- bug: "287076178"
-}
-
-flag {
- namespace: "window_surfaces"
name: "delete_capture_display"
description: "Delete uses of ScreenCapture#captureDisplay"
is_fixed_read_only: true
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4ee03de..cc6460e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5891,6 +5891,17 @@
use glyph bound's as a source of text width. -->
<!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
<attr name="useBoundsForWidth" format="boolean" />
+
+
+ <!-- Whether to shift the drawing offset for prevent clipping start drawing offset.
+ This value is ignored when the useBoundsForWidth attribute is false.
+
+ If this value is false, the TextView draws text from the zero X coordinate. This is
+ useful for aligning multiple TextViews vertically.
+ If this value is true, the TextView shift the drawing offset not to clip the
+ stroke in the region where the X coordinate is negative. -->
+ <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
+ <attr name="shiftDrawingOffsetForStartOverhang" format="boolean" />
<!-- Whether to use the locale preferred line height for the minimum line height.
This flag is useful for preventing jitter of entering letters into empty EditText.
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 13ebae1..8af8cb8 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -163,6 +163,8 @@
<public name="defaultToObserveMode"/>
<!-- @FlaggedApi("android.security.asm_restrictions_enabled") -->
<public name="allowCrossUidActivitySwitchFromBelow"/>
+ <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
+ <public name="shiftDrawingOffsetForStartOverhang" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01bc0000">
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 2327b20..48ef7e6 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -40,6 +40,7 @@
import android.app.Application;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
+import android.app.PictureInPictureUiState;
import android.app.ResourcesManager;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
@@ -706,6 +707,9 @@
final TestActivity activity = mActivityTestRule.launchActivity(startIntent);
final ActivityThread activityThread = activity.getActivityThread();
final ActivityClientRecord r = getActivityClientRecord(activity);
+ if (android.app.Flags.enablePipUiStateCallbackOnEntering()) {
+ activity.mPipUiStateLatch = new CountDownLatch(1);
+ }
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
activityThread.handlePictureInPictureRequested(r);
@@ -940,6 +944,11 @@
* latch reaches 0.
*/
volatile CountDownLatch mConfigLatch;
+ /**
+ * A latch used to notify tests that we're about to wait for the
+ * onPictureInPictureUiStateChanged callback.
+ */
+ volatile CountDownLatch mPipUiStateLatch;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -974,6 +983,14 @@
if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_ENTER, false)) {
enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
mPipEntered = true;
+ // Await for onPictureInPictureUiStateChanged callback if applicable
+ if (mPipUiStateLatch != null) {
+ try {
+ mPipUiStateLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
return true;
} else if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_SKIP, false)) {
mPipEnterSkipped = true;
@@ -982,6 +999,13 @@
return super.onPictureInPictureRequested();
}
+ @Override
+ public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) {
+ if (mPipUiStateLatch != null && pipState.isEnteringPip()) {
+ mPipUiStateLatch.countDown();
+ }
+ }
+
boolean pipRequested() {
return mPipRequested;
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 36c7f54..433d353 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -660,10 +660,22 @@
public void votePreferredFrameRate_voteFrameRateCategory_aggregate() {
View view = new View(sContext);
attachViewToWindow(view);
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ });
+
+ // reset the frame rate category counts
+ for (int i = 0; i < 5; i++) {
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+ }
+
+ sInstrumentation.runOnMainSync(() -> {
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
@@ -723,6 +735,18 @@
sInstrumentation.runOnMainSync(() -> {
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ });
+
+ // reset the frame rate category counts
+ for (int i = 0; i < 5; i++) {
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+ }
+
+ sInstrumentation.runOnMainSync(() -> {
view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW);
view.invalidate();
assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
@@ -849,7 +873,18 @@
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NO_PREFERENCE);
assertEquals(viewRootImpl.getPreferredFrameRate(), frameRate, 0.1);
+ });
+ // reset the frame rate category counts
+ for (int i = 0; i < 5; i++) {
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+ }
+
+ sInstrumentation.runOnMainSync(() -> {
view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW);
view.invalidate();
assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
@@ -884,18 +919,6 @@
ViewRootImpl viewRootImpl = view.getViewRootImpl();
- // Frequent update
- sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
- });
-
// In transistion from frequent update to infrequent update
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
@@ -903,9 +926,28 @@
assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
});
+ // reset the frame rate category counts
+ for (int i = 0; i < 5; i++) {
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+ }
+
+ // In transistion from frequent update to infrequent update
+ Thread.sleep(delay);
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ });
+
// Infrequent update
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
view.invalidate();
assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
});
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index c8cbb98..42e3387 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2077,6 +2077,12 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
+ "-315778658": {
+ "message": "transferTouchGesture failed because args transferFromToken or transferToToken is null",
+ "level": "ERROR",
+ "group": "WM_DEBUG_EMBEDDED_WINDOWS",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"-312353598": {
"message": "Executing finish of activity: %s",
"level": "VERBOSE",
@@ -2293,12 +2299,6 @@
"group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "-90559682": {
- "message": "Config is skipping already pausing %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-87705714": {
"message": "findFocusedWindow: focusedApp=null using new focus @ %s",
"level": "VERBOSE",
@@ -3547,12 +3547,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1011462000": {
- "message": "Re-launching after pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/TaskFragment.java"
- },
"1015746067": {
"message": "Display id=%d is ignoring orientation request for %d, return %d following a per-app override for %s",
"level": "VERBOSE",
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt
new file mode 100644
index 0000000..aad593e
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 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.systemui.surfaceeffects.loadingeffect
+
+import android.content.Context
+import android.graphics.BlendMode
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.View
+
+/** Custom View for drawing the [LoadingEffect] with [Canvas.drawPaint]. */
+open class LoadingEffectView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
+
+ private var paint: Paint? = null
+ private var blendMode: BlendMode = BlendMode.SRC_OVER
+
+ override fun onDraw(canvas: Canvas) {
+ if (!canvas.isHardwareAccelerated) {
+ return
+ }
+ paint?.let { canvas.drawPaint(it) }
+ }
+
+ /** Designed to be called on [LoadingEffect.PaintDrawCallback.onDraw]. */
+ fun draw(paint: Paint) {
+ this.paint = paint
+ this.paint!!.blendMode = blendMode
+
+ invalidate()
+ }
+
+ /** Sets the blend mode of the [Paint]. */
+ fun setBlendMode(blendMode: BlendMode) {
+ this.blendMode = blendMode
+ }
+}
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 5db9eee..109e63c 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -67,6 +67,18 @@
android:background="@drawable/qs_media_outline_layout_bg"
/>
+ <com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
+ android:id="@+id/loading_effect_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_expanded"
+ app:layout_constraintStart_toStartOf="@id/album_art"
+ app:layout_constraintEnd_toEndOf="@id/album_art"
+ app:layout_constraintTop_toTopOf="@id/album_art"
+ app:layout_constraintBottom_toBottomOf="@id/album_art"
+ android:clipToOutline="true"
+ android:background="@drawable/qs_media_outline_layout_bg"
+ />
+
<!-- Guideline for output switcher -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/center_vertical_guideline"
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index c053b33..2f2b10f 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -55,6 +55,15 @@
app:layout_constraintBottom_toBottomOf="@+id/album_art" />
<Constraint
+ android:id="@+id/loading_effect_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_collapsed"
+ app:layout_constraintStart_toStartOf="@+id/album_art"
+ app:layout_constraintEnd_toEndOf="@+id/album_art"
+ app:layout_constraintTop_toTopOf="@+id/album_art"
+ app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+
+ <Constraint
android:id="@+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml
index 8bf7560d..0140d52 100644
--- a/packages/SystemUI/res/xml/media_session_expanded.xml
+++ b/packages/SystemUI/res/xml/media_session_expanded.xml
@@ -48,6 +48,15 @@
app:layout_constraintBottom_toBottomOf="@+id/album_art" />
<Constraint
+ android:id="@+id/loading_effect_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_expanded"
+ app:layout_constraintStart_toStartOf="@+id/album_art"
+ app:layout_constraintEnd_toEndOf="@+id/album_art"
+ app:layout_constraintTop_toTopOf="@+id/album_art"
+ app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+
+ <Constraint
android:id="@+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
index 1b14f75..898eacf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
@@ -25,8 +25,9 @@
import android.widget.TextView
import androidx.constraintlayout.widget.Barrier
import com.android.internal.widget.CachingIconView
-import com.android.systemui.res.R
import com.android.systemui.media.controls.models.GutsViewHolder
+import com.android.systemui.res.R
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.TransitionLayout
@@ -42,6 +43,7 @@
val multiRippleView = itemView.requireViewById<MultiRippleView>(R.id.touch_ripple_view)
val turbulenceNoiseView =
itemView.requireViewById<TurbulenceNoiseView>(R.id.turbulence_noise_view)
+ val loadingEffectView = itemView.requireViewById<LoadingEffectView>(R.id.loading_effect_view)
val appIcon = itemView.requireViewById<ImageView>(R.id.icon)
val titleText = itemView.requireViewById<TextView>(R.id.header_title)
val artistText = itemView.requireViewById<TextView>(R.id.header_artist)
@@ -171,6 +173,7 @@
setOf(
R.id.album_art,
R.id.turbulence_noise_view,
+ R.id.loading_effect_view,
R.id.touch_ripple_view,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
index c87fd14..952f9b8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
@@ -29,6 +29,7 @@
import com.android.settingslib.Utils
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.monet.ColorScheme
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
import com.android.systemui.surfaceeffects.ripple.MultiRippleController
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
@@ -118,6 +119,7 @@
turbulenceNoiseController,
::AnimatingColorTransition
)
+ var loadingEffect: LoadingEffect? = null
val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_neutral20)
val surfaceColor =
@@ -128,7 +130,6 @@
mediaViewHolder.albumView.backgroundTintList = colorList
mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor)
}
-
val accentPrimary =
animatingColorTransitionFactory(
loadDefaultColor(R.attr.textColorPrimary),
@@ -139,6 +140,7 @@
mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary)
multiRippleController.updateColor(accentPrimary)
turbulenceNoiseController.updateNoiseColor(accentPrimary)
+ loadingEffect?.updateColor(accentPrimary)
}
val accentSecondary =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index aa92814..e97c9d3d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -41,6 +41,7 @@
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
+import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.BitmapDrawable;
@@ -81,6 +82,7 @@
import com.android.internal.widget.CachingIconView;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.ActivityIntentHelper;
+import com.android.systemui.Flags;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.GhostedViewTransitionAnimatorController;
import com.android.systemui.bluetooth.BroadcastDialogController;
@@ -111,6 +113,9 @@
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect;
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState;
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView;
import com.android.systemui.surfaceeffects.ripple.MultiRippleController;
import com.android.systemui.surfaceeffects.ripple.MultiRippleView;
import com.android.systemui.surfaceeffects.ripple.RippleAnimation;
@@ -248,13 +253,34 @@
private String mCurrentBroadcastApp;
private MultiRippleController mMultiRippleController;
private TurbulenceNoiseController mTurbulenceNoiseController;
+ private LoadingEffect mLoadingEffect;
private final GlobalSettings mGlobalSettings;
-
+ private final Random mRandom = new Random();
private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig;
private boolean mWasPlaying = false;
private boolean mButtonClicked = false;
- private final Random mRandom = new Random();
+ private final LoadingEffect.Companion.PaintDrawCallback mNoiseDrawCallback =
+ new LoadingEffect.Companion.PaintDrawCallback() {
+ @Override
+ public void onDraw(@NonNull Paint loadingPaint) {
+ mMediaViewHolder.getLoadingEffectView().draw(loadingPaint);
+ }
+ };
+ private final LoadingEffect.Companion.AnimationStateChangedCallback mStateChangedCallback =
+ new LoadingEffect.Companion.AnimationStateChangedCallback() {
+ @Override
+ public void onStateChanged(@NonNull AnimationState oldState,
+ @NonNull AnimationState newState) {
+ LoadingEffectView loadingEffectView =
+ mMediaViewHolder.getLoadingEffectView();
+ if (newState == AnimationState.NOT_PLAYING) {
+ loadingEffectView.setVisibility(View.INVISIBLE);
+ } else {
+ loadingEffectView.setVisibility(View.VISIBLE);
+ }
+ }
+ };
/**
* Initialize a new control panel
@@ -456,6 +482,10 @@
TurbulenceNoiseView turbulenceNoiseView = vh.getTurbulenceNoiseView();
turbulenceNoiseView.setBlendMode(BlendMode.SCREEN);
+ LoadingEffectView loadingEffectView = vh.getLoadingEffectView();
+ loadingEffectView.setBlendMode(BlendMode.SCREEN);
+ loadingEffectView.setVisibility(View.INVISIBLE);
+
mTurbulenceNoiseController = new TurbulenceNoiseController(turbulenceNoiseView);
mColorSchemeTransition = new ColorSchemeTransition(
@@ -587,22 +617,41 @@
}
}
- // Turbulence noise
if (shouldPlayTurbulenceNoise()) {
+ // Need to create the config here to get the correct view size and color.
if (mTurbulenceNoiseAnimationConfig == null) {
mTurbulenceNoiseAnimationConfig =
- createTurbulenceNoiseAnimation();
+ createTurbulenceNoiseConfig();
}
- // Color will be correctly updated in ColorSchemeTransition.
- mTurbulenceNoiseController.play(
- Type.SIMPLEX_NOISE,
- mTurbulenceNoiseAnimationConfig
- );
- mMainExecutor.executeDelayed(
- mTurbulenceNoiseController::finish,
- TURBULENCE_NOISE_PLAY_DURATION
- );
+
+ if (Flags.shaderlibLoadingEffectRefactor()) {
+ if (mLoadingEffect == null) {
+ mLoadingEffect = new LoadingEffect(
+ Type.SIMPLEX_NOISE,
+ mTurbulenceNoiseAnimationConfig,
+ mNoiseDrawCallback,
+ mStateChangedCallback
+ );
+ mColorSchemeTransition.setLoadingEffect(mLoadingEffect);
+ }
+
+ mLoadingEffect.play();
+ mMainExecutor.executeDelayed(
+ mLoadingEffect::finish,
+ TURBULENCE_NOISE_PLAY_DURATION
+ );
+ } else {
+ mTurbulenceNoiseController.play(
+ Type.SIMPLEX_NOISE,
+ mTurbulenceNoiseAnimationConfig
+ );
+ mMainExecutor.executeDelayed(
+ mTurbulenceNoiseController::finish,
+ TURBULENCE_NOISE_PLAY_DURATION
+ );
+ }
}
+
mButtonClicked = false;
mWasPlaying = isPlaying();
@@ -1232,7 +1281,13 @@
return mButtonClicked && !mWasPlaying && isPlaying();
}
- private TurbulenceNoiseAnimationConfig createTurbulenceNoiseAnimation() {
+ private TurbulenceNoiseAnimationConfig createTurbulenceNoiseConfig() {
+ View targetView = Flags.shaderlibLoadingEffectRefactor()
+ ? mMediaViewHolder.getLoadingEffectView() :
+ mMediaViewHolder.getTurbulenceNoiseView();
+ int width = targetView.getWidth();
+ int height = targetView.getHeight();
+
return new TurbulenceNoiseAnimationConfig(
/* gridCount= */ 2.14f,
TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER,
@@ -1242,10 +1297,11 @@
/* noiseMoveSpeedX= */ 0.42f,
/* noiseMoveSpeedY= */ 0f,
TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z,
+ // Color will be correctly updated in ColorSchemeTransition.
/* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(),
/* backgroundColor= */ Color.BLACK,
- /* width= */ mMediaViewHolder.getTurbulenceNoiseView().getWidth(),
- /* height= */ mMediaViewHolder.getTurbulenceNoiseView().getHeight(),
+ width,
+ height,
TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS,
/* easeInDuration= */ 1350f,
/* easeOutDuration= */ 1350f,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index 5996502..c896486 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -40,6 +40,7 @@
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.Bundle
+import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
import android.testing.AndroidTestingRunner
@@ -61,6 +62,7 @@
import com.android.internal.logging.InstanceId
import com.android.internal.widget.CachingIconView
import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.bluetooth.BroadcastDialogController
import com.android.systemui.broadcast.BroadcastSender
@@ -88,6 +90,7 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
@@ -190,6 +193,7 @@
private lateinit var dismissText: TextView
private lateinit var multiRippleView: MultiRippleView
private lateinit var turbulenceNoiseView: TurbulenceNoiseView
+ private lateinit var loadingEffectView: LoadingEffectView
private lateinit var session: MediaSession
private lateinit var device: MediaDeviceData
@@ -402,6 +406,7 @@
multiRippleView = MultiRippleView(context, null)
turbulenceNoiseView = TurbulenceNoiseView(context, null)
+ loadingEffectView = LoadingEffectView(context, null)
whenever(viewHolder.player).thenReturn(view)
whenever(viewHolder.appIcon).thenReturn(appIcon)
@@ -447,6 +452,7 @@
whenever(viewHolder.multiRippleView).thenReturn(multiRippleView)
whenever(viewHolder.turbulenceNoiseView).thenReturn(turbulenceNoiseView)
+ whenever(viewHolder.loadingEffectView).thenReturn(loadingEffectView)
}
/** Initialize elements for the recommendation view holder */
@@ -2429,6 +2435,7 @@
mainExecutor.execute {
assertThat(turbulenceNoiseView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE)
clock.advanceTime(
MediaControlPanel.TURBULENCE_NOISE_PLAY_DURATION +
@@ -2436,6 +2443,40 @@
)
assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SHADERLIB_LOADING_EFFECT_REFACTOR)
+ fun playTurbulenceNoise_newLoadingEffect_finishesAfterDuration() {
+ val semanticActions =
+ MediaButton(
+ playOrPause =
+ MediaAction(
+ icon = null,
+ action = {},
+ contentDescription = "play",
+ background = null
+ )
+ )
+ val data = mediaData.copy(semanticActions = semanticActions)
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(data, KEY)
+
+ viewHolder.actionPlayPause.callOnClick()
+
+ mainExecutor.execute {
+ assertThat(loadingEffectView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+
+ clock.advanceTime(
+ MediaControlPanel.TURBULENCE_NOISE_PLAY_DURATION +
+ TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS.toLong()
+ )
+
+ assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
}
}
@@ -2458,6 +2499,30 @@
viewHolder.action0.callOnClick()
assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SHADERLIB_LOADING_EFFECT_REFACTOR)
+ fun playTurbulenceNoise_newLoadingEffect_whenPlaybackStateIsNotPlaying_doesNotPlayTurbulence() {
+ val semanticActions =
+ MediaButton(
+ custom0 =
+ MediaAction(
+ icon = null,
+ action = {},
+ contentDescription = "custom0",
+ background = null
+ ),
+ )
+ val data = mediaData.copy(semanticActions = semanticActions)
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(data, KEY)
+
+ viewHolder.action0.callOnClick()
+
+ assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 85d2df3..bbce26c 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -27,11 +27,14 @@
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.SystemProperties;
import android.os.Trace;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -50,9 +53,15 @@
import java.io.File;
import java.io.IOException;
+import java.util.Set;
final class PackageAbiHelperImpl implements PackageAbiHelper {
+ @Nullable
+ private static String[] sNativelySupported32BitAbis = null;
+ @Nullable
+ private static String[] sNativelySupported64BitAbis = null;
+
private static String calculateBundledApkRoot(final String codePathString) {
final File codePath = new File(codePathString);
final File codeRoot;
@@ -122,13 +131,20 @@
}
}
- private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws
- PackageManagerException {
+ private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet,
+ boolean forceMatch) throws PackageManagerException {
if (copyRet < 0) {
if (copyRet != PackageManager.NO_NATIVE_LIBRARIES
&& copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
throw new PackageManagerException(copyRet, message);
}
+
+ if (forceMatch && copyRet == PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS,
+ "The multiArch app's native libs don't support all the natively"
+ + " supported ABIs of the device.");
+ }
}
}
@@ -296,7 +312,40 @@
return new Abis(primaryCpuAbi, secondaryCpuAbi);
}
+ @NonNull
+ private static String[] getNativelySupportedAbis(@NonNull String[] supportedAbis) {
+ Set<String> nativelySupportedAbis = new ArraySet<>();
+ for (int i = 0; i < supportedAbis.length; i++) {
+ final String currentAbi = supportedAbis[i];
+ // In presence of a native bridge this means the Abi is emulated.
+ final String currentIsa = VMRuntime.getInstructionSet(currentAbi);
+ if (TextUtils.isEmpty(SystemProperties.get("ro.dalvik.vm.isa." + currentIsa))) {
+ nativelySupportedAbis.add(currentAbi);
+ }
+ }
+ return nativelySupportedAbis.toArray(new String[0]);
+ }
+
+ private static String[] getNativelySupported32BitAbis() {
+ if (sNativelySupported32BitAbis != null) {
+ return sNativelySupported32BitAbis;
+ }
+
+ sNativelySupported32BitAbis = getNativelySupportedAbis(Build.SUPPORTED_32_BIT_ABIS);
+ return sNativelySupported32BitAbis;
+ }
+
+ private static String[] getNativelySupported64BitAbis() {
+ if (sNativelySupported64BitAbis != null) {
+ return sNativelySupported64BitAbis;
+ }
+
+ sNativelySupported64BitAbis = getNativelySupportedAbis(Build.SUPPORTED_64_BIT_ABIS);
+ return sNativelySupported64BitAbis;
+ }
+
@Override
+ @SuppressWarnings("AndroidFrameworkCompatChange") // the check is before the apk is installed
public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isSystemApp,
boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir)
throws PackageManagerException {
@@ -334,18 +383,33 @@
primaryCpuAbi = null;
secondaryCpuAbi = null;
if (pkg.isMultiArch()) {
+ // Force the match for these cases
+ // 1. pkg.getTargetSdkVersion >= Build.VERSION_CODES.VANILLA_ICE_CREAM
+ // 2. cpuAbiOverride is null. If it is non-null, it is set via shell for testing
+ final boolean forceMatch = Flags.forceMultiArchNativeLibsMatch()
+ && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM
+ && cpuAbiOverride == null;
+
+ String[] supported32BitAbis = forceMatch ? getNativelySupported32BitAbis()
+ : Build.SUPPORTED_32_BIT_ABIS;
+ String[] supported64BitAbis = forceMatch ? getNativelySupported64BitAbis()
+ : Build.SUPPORTED_64_BIT_ABIS;
+
+ final boolean systemSupports32BitAbi = supported32BitAbis.length > 0;
+ final boolean systemSupports64BitAbi = supported64BitAbis.length > 0;
+
int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
- if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+ if (systemSupports32BitAbi) {
if (extractLibs) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+ nativeLibraryRoot, supported32BitAbis,
useIsaSpecificSubdirs, onIncremental);
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
abi32 = NativeLibraryHelper.findSupportedAbi(
- handle, Build.SUPPORTED_32_BIT_ABIS);
+ handle, supported32BitAbis);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -357,24 +421,26 @@
}
maybeThrowExceptionForMultiArchCopy(
- "Error unpackaging 32 bit native libs for multiarch app.", abi32);
+ "Error unpackaging 32 bit native libs for multiarch app.", abi32,
+ forceMatch && systemSupports32BitAbi);
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+ if (systemSupports64BitAbi) {
if (extractLibs) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+ nativeLibraryRoot, supported64BitAbis,
useIsaSpecificSubdirs, onIncremental);
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
abi64 = NativeLibraryHelper.findSupportedAbi(
- handle, Build.SUPPORTED_64_BIT_ABIS);
+ handle, supported64BitAbis);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
maybeThrowExceptionForMultiArchCopy(
- "Error unpackaging 64 bit native libs for multiarch app.", abi64);
+ "Error unpackaging 64 bit native libs for multiarch app.", abi64,
+ forceMatch && systemSupports64BitAbi);
if (abi64 >= 0) {
// Shared library native libs should be in the APK zip aligned
@@ -382,11 +448,11 @@
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Shared library native lib extraction not supported");
}
- primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
+ primaryCpuAbi = supported64BitAbis[abi64];
}
if (abi32 >= 0) {
- final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
+ final String abi = supported32BitAbis[abi32];
if (abi64 >= 0) {
if (pkg.is32BitAbiPreferred()) {
secondaryCpuAbi = primaryCpuAbi;
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index e6ef90b..68bff43 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_EMBEDDED_WINDOWS;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -34,6 +35,9 @@
import android.view.InputChannel;
import android.window.InputTransferToken;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.input.InputManagerService;
+
/**
* Keeps track of embedded windows.
*
@@ -52,9 +56,13 @@
private final Object mGlobalLock;
private final ActivityTaskManagerService mAtmService;
- EmbeddedWindowController(ActivityTaskManagerService atmService) {
+ private final InputManagerService mInputManagerService;
+
+ EmbeddedWindowController(ActivityTaskManagerService atmService,
+ InputManagerService inputManagerService) {
mAtmService = atmService;
mGlobalLock = atmService.getGlobalLock();
+ mInputManagerService = inputManagerService;
}
/**
@@ -135,6 +143,60 @@
return mWindowsByWindowToken.get(windowToken);
}
+ private boolean isValidTouchGestureParams(WindowState hostWindowState,
+ EmbeddedWindow embeddedWindow) {
+ if (embeddedWindow == null) {
+ ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS,
+ "Attempt to transfer touch gesture with non-existent embedded window");
+ return false;
+ }
+ final WindowState wsAssociatedWithEmbedded = embeddedWindow.getWindowState();
+ if (wsAssociatedWithEmbedded == null) {
+ ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS,
+ "Attempt to transfer touch gesture using embedded window with no associated "
+ + "host");
+ return false;
+ }
+ if (wsAssociatedWithEmbedded.mClient.asBinder() != hostWindowState.mClient.asBinder()) {
+ ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS,
+ "Attempt to transfer touch gesture with host window not associated with "
+ + "embedded window");
+ return false;
+ }
+
+ if (embeddedWindow.getInputChannelToken() == null) {
+ ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS,
+ "Attempt to transfer touch gesture using embedded window that has no input "
+ + "channel");
+ return false;
+ }
+ if (hostWindowState.mInputChannelToken == null) {
+ ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS,
+ "Attempt to transfer touch gesture using a host window with no input channel");
+ return false;
+ }
+ return true;
+ }
+
+ boolean transferToHost(InputTransferToken embeddedWindowToken,
+ WindowState transferToHostWindowState) {
+ EmbeddedWindow ew = getByInputTransferToken(embeddedWindowToken);
+ if (!isValidTouchGestureParams(transferToHostWindowState, ew)) {
+ return false;
+ }
+ return mInputManagerService.transferTouchFocus(ew.getInputChannelToken(),
+ transferToHostWindowState.mInputChannelToken);
+ }
+
+ boolean transferToEmbedded(WindowState hostWindowState, InputTransferToken transferToToken) {
+ final EmbeddedWindowController.EmbeddedWindow ew = getByInputTransferToken(transferToToken);
+ if (!isValidTouchGestureParams(hostWindowState, ew)) {
+ return false;
+ }
+ return mInputManagerService.transferTouchFocus(hostWindowState.mInputChannelToken,
+ ew.getInputChannelToken());
+ }
+
static class EmbeddedWindow implements InputTarget {
final IBinder mClient;
@Nullable final WindowState mHostWindowState;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 3c8c55e..975208f 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -970,40 +970,6 @@
}
@Override
- public boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
- if (embeddedWindow == null) {
- return false;
- }
-
- final long identity = Binder.clearCallingIdentity();
- boolean didTransfer = false;
- try {
- didTransfer = mService.transferEmbeddedTouchFocusToHost(embeddedWindow);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return didTransfer;
- }
-
- @Override
- public boolean transferHostTouchGestureToEmbedded(IWindow hostWindow,
- InputTransferToken inputTransferToken) {
- if (hostWindow == null) {
- return false;
- }
-
- final long identity = Binder.clearCallingIdentity();
- boolean didTransfer;
- try {
- didTransfer = mService.transferHostTouchGestureToEmbedded(this, hostWindow,
- inputTransferToken);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return didTransfer;
- }
-
- @Override
public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 520f6cb..3d6bd4f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -101,6 +101,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_EMBEDDED_WINDOWS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
@@ -1344,7 +1345,7 @@
LocalServices.addService(WindowManagerInternal.class, new LocalService());
LocalServices.addService(
ImeTargetVisibilityPolicy.class, new ImeTargetVisibilityPolicyImpl());
- mEmbeddedWindowController = new EmbeddedWindowController(mAtmService);
+ mEmbeddedWindowController = new EmbeddedWindowController(mAtmService, inputManager);
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
mContext.getResources());
@@ -9055,73 +9056,37 @@
null /* region */, clientToken);
}
- boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
- final IBinder windowBinder = embeddedWindow.asBinder();
- final IBinder hostInputChannel, embeddedInputChannel;
- synchronized (mGlobalLock) {
- final EmbeddedWindowController.EmbeddedWindow ew =
- mEmbeddedWindowController.getByWindowToken(windowBinder);
- if (ew == null) {
- Slog.w(TAG, "Attempt to transfer touch focus from non-existent embedded window");
- return false;
- }
- final WindowState hostWindowState = ew.getWindowState();
- if (hostWindowState == null) {
- Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no" +
- " associated host");
- return false;
- }
- embeddedInputChannel = ew.getInputChannelToken();
- if (embeddedInputChannel == null) {
- Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input" +
- " channel");
- return false;
- }
- hostInputChannel = hostWindowState.mInputChannelToken;
- if (hostInputChannel == null) {
- Slog.w(TAG, "Attempt to transfer touch focus to a host window with no" +
- " input channel");
- return false;
- }
- return mInputManager.transferTouchFocus(embeddedInputChannel, hostInputChannel);
+ @Override
+ public boolean transferTouchGesture(InputTransferToken transferFromToken,
+ InputTransferToken transferToToken) {
+ if (transferFromToken == null || transferToToken == null) {
+ ProtoLog.e(WM_DEBUG_EMBEDDED_WINDOWS,
+ "transferTouchGesture failed because args transferFromToken or "
+ + "transferToToken is null");
+ return false;
}
- }
- boolean transferHostTouchGestureToEmbedded(Session session, IWindow hostWindow,
- InputTransferToken inputTransferToken) {
- final IBinder hostInputChannel, embeddedInputChannel;
- synchronized (mGlobalLock) {
- final WindowState hostWindowState = windowForClientLocked(session, hostWindow, false);
- if (hostWindowState == null) {
- Slog.w(TAG, "Attempt to transfer touch gesture with invalid host window");
- return false;
+ final long identity = Binder.clearCallingIdentity();
+ boolean didTransfer;
+ try {
+ synchronized (mGlobalLock) {
+ // If the transferToToken exists in the input to window map, it means the request
+ // is to transfer from embedded to host. Otherwise, the transferToToken
+ // represents an embedded window so transfer from host to embedded.
+ WindowState windowStateTo = mInputToWindowMap.get(transferToToken.mToken);
+ if (windowStateTo != null) {
+ didTransfer = mEmbeddedWindowController.transferToHost(transferFromToken,
+ windowStateTo);
+ } else {
+ WindowState windowStateFrom = mInputToWindowMap.get(transferFromToken.mToken);
+ didTransfer = mEmbeddedWindowController.transferToEmbedded(windowStateFrom,
+ transferToToken);
+ }
}
-
- final EmbeddedWindowController.EmbeddedWindow ew =
- mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
- if (ew == null || ew.mHostWindowState == null) {
- Slog.w(TAG, "Attempt to transfer touch gesture to non-existent embedded window");
- return false;
- }
- if (ew.mHostWindowState.mClient.asBinder() != hostWindow.asBinder()) {
- Slog.w(TAG, "Attempt to transfer touch gesture to embedded window not associated"
- + " with host window");
- return false;
- }
- embeddedInputChannel = ew.getInputChannelToken();
- if (embeddedInputChannel == null) {
- Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input"
- + " channel");
- return false;
- }
- hostInputChannel = hostWindowState.mInputChannelToken;
- if (hostInputChannel == null) {
- Slog.w(TAG,
- "Attempt to transfer touch focus to a host window with no input channel");
- return false;
- }
- return mInputManager.transferTouchFocus(hostInputChannel, embeddedInputChannel);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
+ return didTransfer;
}
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,