Merge "Address difference in icon id with provider model" into sc-dev
diff --git a/Android.bp b/Android.bp
index 77044d6..f9320eb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -510,15 +510,6 @@
],
}
-filegroup {
- name: "framework-all-sources",
- srcs: [
- ":framework-mime-sources",
- ":framework-non-updatable-sources",
- ":framework-updatable-sources",
- ],
-}
-
// AIDL files under these paths are mixture of public and private ones.
// They shouldn't be exported across module boundaries.
java_defaults {
@@ -1391,14 +1382,6 @@
],
}
-// Creates an index of AIDL methods; used for adding UnsupportedAppUsage
-// annotations to private apis
-aidl_mapping {
- name: "framework-aidl-mappings",
- srcs: [":framework-all-sources"],
- output: "framework-aidl-mappings.txt",
-}
-
// Avoid including Parcelable classes as we don't want to have two copies of
// Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony)
// and TeleService app (packages/services/Telephony).
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index ad68169..8d83309 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -25,7 +25,9 @@
}
public static final class MediaTranscodeManager.TranscodingSession {
+ method public void addClientUid(int);
method public void cancel();
+ method @NonNull public java.util.List<java.lang.Integer> getClientUids();
method public int getErrorCode();
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index 7f4685e..fef4865 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -1352,6 +1352,8 @@
private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
@GuardedBy("mLock")
private boolean mHasRetried = false;
+ @GuardedBy("mLock")
+ private @NonNull List<Integer> mClientUidList = new ArrayList<>();
// The original request that associated with this session.
private final TranscodingRequest mRequest;
@@ -1370,6 +1372,7 @@
mListenerExecutor = executor;
mListener = listener;
mRequest = request;
+ mClientUidList.add(request.getClientUid());
}
/**
@@ -1515,6 +1518,36 @@
}
/**
+ * Adds a client uid that is also waiting for this transcoding session.
+ * <p>
+ * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could add the
+ * uid. Note that the permission check happens on the service side upon starting the
+ * transcoding. If the client does not have the permission, the transcoding will fail.
+ */
+ public void addClientUid(int uid) {
+ if (uid < 0) {
+ throw new IllegalArgumentException("Invalid Uid");
+ }
+ synchronized (mLock) {
+ if (!mClientUidList.contains(uid)) {
+ // see ag/14023202 for implementation
+ mClientUidList.add(uid);
+ }
+ }
+ }
+
+ /**
+ * Query all the client that waiting for this transcoding session
+ * @return a list containing all the client uids.
+ */
+ @NonNull
+ public List<Integer> getClientUids() {
+ synchronized (mLock) {
+ return mClientUidList;
+ }
+ }
+
+ /**
* Gets sessionId of the transcoding session.
* @return session id.
*/
diff --git a/core/api/current.txt b/core/api/current.txt
index 48fa5dc..f8654478 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -51550,6 +51550,7 @@
field public static final int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5; // 0xfffffffb
field public static final int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; // 0xfffffffd
field public static final int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; // 0xfffffffc
+ field public static final int DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS = -6; // 0xfffffffa
field public static final int DISPLAY_HASH_ERROR_UNKNOWN = -1; // 0xffffffff
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a0efbb8..d0f9094 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -321,6 +321,7 @@
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
field public static final int sdkVersion = 16844304; // 0x1010610
field public static final int supportsAmbientMode = 16844173; // 0x101058d
+ field public static final int throttleDurationMillis = 16844360; // 0x1010648
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -9794,6 +9795,7 @@
method @NonNull public abstract java.util.Map<java.lang.String,android.service.displayhash.DisplayHashParams> onGetDisplayHashAlgorithms();
method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash);
field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHasherService";
+ field public static final String SERVICE_META_DATA = "android.displayhash.display_hasher_service";
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 04541be..56b0a9d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2877,6 +2877,14 @@
}
+package android.view.displayhash {
+
+ public final class DisplayHashManager {
+ method @RequiresPermission("android.permission.READ_FRAME_BUFFER") public void setDisplayHashThrottlingEnabled(boolean);
+ }
+
+}
+
package android.view.inputmethod {
public final class InlineSuggestion implements android.os.Parcelable {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 16a8fa8..22d74ca 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9176,6 +9176,22 @@
public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
/**
+ * Whether the assistant can be triggered by a touch gesture.
+ *
+ * @hide
+ */
+ public static final String ASSIST_TOUCH_GESTURE_ENABLED =
+ "assist_touch_gesture_enabled";
+
+ /**
+ * Whether the assistant can be triggered by long-pressing the home button
+ *
+ * @hide
+ */
+ public static final String ASSIST_LONG_PRESS_HOME_ENABLED =
+ "assist_long_press_home_enabled";
+
+ /**
* Control whether Trust Agents are in active unlock or extend unlock mode.
* @hide
*/
diff --git a/core/java/android/service/displayhash/DisplayHasherService.java b/core/java/android/service/displayhash/DisplayHasherService.java
index 2105d84..d300cf1 100644
--- a/core/java/android/service/displayhash/DisplayHasherService.java
+++ b/core/java/android/service/displayhash/DisplayHasherService.java
@@ -52,6 +52,16 @@
"android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
/**
+ * Name under which a DisplayHasherService component publishes information
+ * about itself. This meta-data must reference an XML resource containing a
+ * {@link com.android.internal.R.styleable#DisplayHasherService} tag.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SERVICE_META_DATA = "android.displayhash.display_hasher_service";
+
+ /**
* The {@link Intent} action that must be declared as handled by a service in its manifest
* for the system to recognize it as a DisplayHash providing service.
*
diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java
index 547bc9d..b55d9b3 100644
--- a/core/java/android/view/ContentInfo.java
+++ b/core/java/android/view/ContentInfo.java
@@ -26,6 +26,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.util.Pair;
+import android.view.inputmethod.InputContentInfo;
import com.android.internal.util.Preconditions;
@@ -141,6 +142,10 @@
private final Uri mLinkUri;
@Nullable
private final Bundle mExtras;
+ @Nullable
+ private final InputContentInfo mInputContentInfo;
+ @Nullable
+ private final DragAndDropPermissions mDragAndDropPermissions;
private ContentInfo(Builder b) {
this.mClip = Objects.requireNonNull(b.mClip);
@@ -149,6 +154,23 @@
this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
this.mLinkUri = b.mLinkUri;
this.mExtras = b.mExtras;
+ this.mInputContentInfo = b.mInputContentInfo;
+ this.mDragAndDropPermissions = b.mDragAndDropPermissions;
+ }
+
+ /**
+ * If the content came from a source that supports proactive release of URI permissions
+ * (e.g. IME), releases permissions; otherwise a no-op.
+ *
+ * @hide
+ */
+ public void releasePermissions() {
+ if (mInputContentInfo != null) {
+ mInputContentInfo.releasePermission();
+ }
+ if (mDragAndDropPermissions != null) {
+ mDragAndDropPermissions.release();
+ }
}
@NonNull
@@ -275,6 +297,10 @@
private Uri mLinkUri;
@Nullable
private Bundle mExtras;
+ @Nullable
+ private InputContentInfo mInputContentInfo;
+ @Nullable
+ private DragAndDropPermissions mDragAndDropPermissions;
/**
* Creates a new builder initialized with the data from the given builder.
@@ -285,6 +311,8 @@
mFlags = other.mFlags;
mLinkUri = other.mLinkUri;
mExtras = other.mExtras;
+ mInputContentInfo = other.mInputContentInfo;
+ mDragAndDropPermissions = other.mDragAndDropPermissions;
}
/**
@@ -355,6 +383,31 @@
}
/**
+ * Set the {@link InputContentInfo} object if the content is coming from the IME. This can
+ * be used for proactive cleanup of permissions.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setInputContentInfo(@Nullable InputContentInfo inputContentInfo) {
+ mInputContentInfo = inputContentInfo;
+ return this;
+ }
+
+ /**
+ * Set the {@link DragAndDropPermissions} object if the content is coming via drag-and-drop.
+ * This can be used for proactive cleanup of permissions.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setDragAndDropPermissions(@Nullable DragAndDropPermissions permissions) {
+ mDragAndDropPermissions = permissions;
+ return this;
+ }
+
+
+ /**
* @return A new {@link ContentInfo} instance with the data from this builder.
*/
@NonNull
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e1f13f2..d0a3e4b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -777,6 +777,15 @@
VerifiedDisplayHash verifyDisplayHash(in DisplayHash displayHash);
/**
+ * Call to enable or disable the throttling when generating a display hash. This should only be
+ * used for testing. Throttling is enabled by default.
+ *
+ * Must be called from a process that has {@link android.Manifest.permission#READ_FRAME_BUFFER}
+ * permission.
+ */
+ void setDisplayHashThrottlingEnabled(boolean enable);
+
+ /**
* Registers a listener for a {@link android.window.WindowContext} to handle configuration
* changes from the server side.
* <p>
diff --git a/core/java/android/view/RoundedCorner.java b/core/java/android/view/RoundedCorner.java
index 56b4383..d10fc44 100644
--- a/core/java/android/view/RoundedCorner.java
+++ b/core/java/android/view/RoundedCorner.java
@@ -29,16 +29,10 @@
/**
* Represents a rounded corner of the display.
- *
- * <code>
- * ________
- * / ^
- * / | Radius
- * | v
- * | X <- Center point
- * |<----->
- * Radius
- * </code>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/rounded_corner/rounded-corner-info.png" height="120"
+ * alt="A figure to describe what the rounded corner radius and the center point are. "/>
+ * </p>
*
* <p>Note: The rounded corner formed by the radius and the center is an approximation.</p>
*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 615dd82..63a7dea 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -26801,8 +26801,10 @@
if (permissions != null) {
permissions.takeTransient();
}
- final ContentInfo payload = new ContentInfo.Builder(
- event.getClipData(), SOURCE_DRAG_AND_DROP).build();
+ final ContentInfo payload =
+ new ContentInfo.Builder(event.getClipData(), SOURCE_DRAG_AND_DROP)
+ .setDragAndDropPermissions(permissions)
+ .build();
ContentInfo remainingPayload = performReceiveContent(payload);
// Return true unless none of the payload was consumed.
return remainingPayload != payload;
diff --git a/core/java/android/view/displayhash/DisplayHashManager.java b/core/java/android/view/displayhash/DisplayHashManager.java
index 6b0c1a6..69dfc38 100644
--- a/core/java/android/view/displayhash/DisplayHashManager.java
+++ b/core/java/android/view/displayhash/DisplayHashManager.java
@@ -21,7 +21,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
@@ -94,4 +96,19 @@
return null;
}
}
+
+ /**
+ * Call to enable or disable the throttling when generating a display hash. This should only be
+ * used for testing. Throttling is enabled by default.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.READ_FRAME_BUFFER)
+ public void setDisplayHashThrottlingEnabled(boolean enable) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().setDisplayHashThrottlingEnabled(enable);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/view/displayhash/DisplayHashResultCallback.java b/core/java/android/view/displayhash/DisplayHashResultCallback.java
index 04d29ee..6e3d9a8 100644
--- a/core/java/android/view/displayhash/DisplayHashResultCallback.java
+++ b/core/java/android/view/displayhash/DisplayHashResultCallback.java
@@ -72,13 +72,20 @@
*/
int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5;
+ /**
+ * The caller requested to generate the hash too frequently. The caller should try again at a
+ * after some time has passed to ensure the system isn't overloaded.
+ */
+ int DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS = -6;
+
/** @hide */
@IntDef(prefix = {"DISPLAY_HASH_ERROR_"}, value = {
DISPLAY_HASH_ERROR_UNKNOWN,
DISPLAY_HASH_ERROR_INVALID_BOUNDS,
DISPLAY_HASH_ERROR_MISSING_WINDOW,
DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN,
- DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM
+ DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM,
+ DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS
})
@Retention(RetentionPolicy.SOURCE)
@interface DisplayHashErrorCode {
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index c5bce28..9f796c0 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -959,10 +959,10 @@
}
final ClipData clip = new ClipData(inputContentInfo.getDescription(),
new ClipData.Item(inputContentInfo.getContentUri()));
- final ContentInfo payload =
- new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD)
+ final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD)
.setLinkUri(inputContentInfo.getLinkUri())
.setExtras(opts)
+ .setInputContentInfo(inputContentInfo)
.build();
return mTargetView.performReceiveContent(payload) == null;
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 238ce85..48e25c51 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2894,8 +2894,8 @@
final int originalLength = mTextView.getText().length();
Selection.setSelection((Spannable) mTextView.getText(), offset);
final ClipData clip = event.getClipData();
- final ContentInfo payload =
- new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP)
+ final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP)
+ .setDragAndDropPermissions(permissions)
.build();
mTextView.performReceiveContent(payload);
if (dragDropIntoItself) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index cb2bba1..29c78b5 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -6342,6 +6342,9 @@
/**
* Set the ID of the top-level view of the XML layout.
*
+ * The view's ID is changed right after inflation, before it gets added to its parent. The ID
+ * of a given view can never change after the initial inflation.
+ *
* Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
*
* @throws UnsupportedOperationException if the method is called on a RemoteViews defined in
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index f0badbe..530cb44 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -123,6 +123,8 @@
optional SettingProto gesture_silence_alerts_enabled = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto gesture_wake_enabled = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto gesture_setup_complete = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto touch_gesture_enabled = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto long_press_home_enabled = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Assist assist = 7;
diff --git a/core/res/res/layout/chooser_action_button.xml b/core/res/res/layout/chooser_action_button.xml
index 16ffaa4..def429e 100644
--- a/core/res/res/layout/chooser_action_button.xml
+++ b/core/res/res/layout/chooser_action_button.xml
@@ -19,13 +19,13 @@
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:drawablePadding="8dp"
- android:textColor="?android:textColorPrimary"
+ android:textColor="@color/text_color_primary_device_default_light"
android:textSize="12sp"
android:maxWidth="192dp"
android:singleLine="true"
android:clickable="true"
android:background="@drawable/chooser_action_button_bg"
- android:drawableTint="?android:textColorPrimary"
+ android:drawableTint="@color/text_color_primary_device_default_light"
android:drawableTintMode="src_in"
style="?android:attr/borderlessButtonStyle"
/>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 42c2c69..ca549ae 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -30,9 +30,7 @@
<!-- Height of the status bar -->
<dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen>
<!-- Height of area above QQS where battery/time go -->
- <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
- <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
- <dimen name="quick_qs_total_height">152dp</dimen>
+ <dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Default height of an action bar. -->
<dimen name="action_bar_default_height">40dip</dimen>
<!-- Vertical padding around action bar icons. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 74f8ff6..eea3799 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9539,4 +9539,10 @@
<attr name="iconfactoryBadgeSize" format="dimension"/>
<!-- Perceptual luminance of a color, in accessibility friendly color space. From 0 to 100. -->
<attr name="lStar" format="float"/>
+
+ <declare-styleable name="DisplayHasherService">
+ <!-- The throttle duration for display hash requests
+ @hide @SystemApi -->
+ <attr name="throttleDurationMillis" format="integer" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index afbbe46..cf5b4e1 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -47,10 +47,6 @@
<dimen name="status_bar_height_landscape">@dimen/status_bar_height_portrait</dimen>
<!-- Height of area above QQS where battery/time go -->
<dimen name="quick_qs_offset_height">48dp</dimen>
- <!-- Total height of QQS (quick_qs_offset_height + 128) -->
- <dimen name="quick_qs_total_height">176dp</dimen>
- <!-- Total height of QQS with two rows to fit media player (quick_qs_offset_height + 176) -->
- <dimen name="quick_qs_total_height_with_media">224dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_height">48dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 49039f9..aea9d60 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3096,6 +3096,8 @@
<!-- @hide @SystemApi -->
<public name="playHomeTransitionSound" />
<public name="lStar" />
+ <!-- @hide @SystemApi -->
+ <public name="throttleDurationMillis" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8621147..beed9a3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1716,8 +1716,6 @@
<java-symbol type="dimen" name="status_bar_height" />
<java-symbol type="dimen" name="display_cutout_touchable_region_size" />
<java-symbol type="dimen" name="quick_qs_offset_height" />
- <java-symbol type="dimen" name="quick_qs_total_height" />
- <java-symbol type="dimen" name="quick_qs_total_height_with_media" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
<java-symbol type="drawable" name="ic_jog_dial_unlock" />
diff --git a/docs/html/reference/images/rounded_corner/rounded-corner-info.png b/docs/html/reference/images/rounded_corner/rounded-corner-info.png
new file mode 100644
index 0000000..312d935
--- /dev/null
+++ b/docs/html/reference/images/rounded_corner/rounded-corner-info.png
Binary files differ
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 168a863..1e90b7c 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1379,6 +1379,11 @@
enum : uint32_t {
// Additional flag indicating an entry is public.
SPEC_PUBLIC = 0x40000000u,
+
+ // Additional flag indicating the resource id for this resource may change in a future
+ // build. If this flag is set, the SPEC_PUBLIC flag is also set since the resource must be
+ // public to be exposed as an API to other applications.
+ SPEC_STAGED_API = 0x20000000u,
};
};
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 0a9560a..7428c6e 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -311,6 +311,7 @@
field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d
field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
+ field public static final int NET_CAPABILITY_HEAD_UNIT = 32; // 0x20
field public static final int NET_CAPABILITY_IA = 7; // 0x7
field public static final int NET_CAPABILITY_IMS = 4; // 0x4
field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
@@ -334,6 +335,7 @@
field public static final int TRANSPORT_CELLULAR = 0; // 0x0
field public static final int TRANSPORT_ETHERNET = 3; // 0x3
field public static final int TRANSPORT_LOWPAN = 6; // 0x6
+ field public static final int TRANSPORT_USB = 8; // 0x8
field public static final int TRANSPORT_VPN = 4; // 0x4
field public static final int TRANSPORT_WIFI = 1; // 0x1
field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index ca69f16..4f95ccc 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -275,6 +275,7 @@
NET_CAPABILITY_ENTERPRISE,
NET_CAPABILITY_VSIM,
NET_CAPABILITY_BIP,
+ NET_CAPABILITY_HEAD_UNIT,
})
public @interface NetCapability { }
@@ -508,8 +509,13 @@
@SystemApi
public static final int NET_CAPABILITY_BIP = 31;
+ /**
+ * Indicates that this network is connected to an automotive head unit.
+ */
+ public static final int NET_CAPABILITY_HEAD_UNIT = 32;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_BIP;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_HEAD_UNIT;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -527,7 +533,10 @@
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
| (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
- | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED)
+ // The value of NET_CAPABILITY_HEAD_UNIT is 32, which cannot use int to do bit shift,
+ // otherwise there will be an overflow. Use long to do bit shift instead.
+ | (1L << NET_CAPABILITY_HEAD_UNIT);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -867,6 +876,7 @@
TRANSPORT_WIFI_AWARE,
TRANSPORT_LOWPAN,
TRANSPORT_TEST,
+ TRANSPORT_USB,
})
public @interface Transport { }
@@ -913,10 +923,15 @@
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int TRANSPORT_TEST = 7;
+ /**
+ * Indicates this network uses a USB transport.
+ */
+ public static final int TRANSPORT_USB = 8;
+
/** @hide */
public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
/** @hide */
- public static final int MAX_TRANSPORT = TRANSPORT_TEST;
+ public static final int MAX_TRANSPORT = TRANSPORT_USB;
/** @hide */
public static boolean isValidTransport(@Transport int transportType) {
@@ -931,7 +946,8 @@
"VPN",
"WIFI_AWARE",
"LOWPAN",
- "TEST"
+ "TEST",
+ "USB"
};
/**
@@ -2107,6 +2123,7 @@
case NET_CAPABILITY_ENTERPRISE: return "ENTERPRISE";
case NET_CAPABILITY_VSIM: return "VSIM";
case NET_CAPABILITY_BIP: return "BIP";
+ case NET_CAPABILITY_HEAD_UNIT: return "HEAD_UNIT";
default: return Integer.toString(capability);
}
}
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index b44128b..518f198 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -72,7 +72,7 @@
"ServiceConnectivityResources",
],
static_libs: [
- "dnsresolver_aidl_interface-V7-java",
+ "dnsresolver_aidl_interface-V8-java",
"modules-utils-os",
"net-utils-device-common",
"net-utils-framework-common",
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index db9b83e..53920f0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -165,6 +165,8 @@
VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ASSIST_TOUCH_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ASSIST_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"}));
VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 9c67e9c..4119dc9f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1881,6 +1881,12 @@
dumpSetting(s, p,
Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED,
+ SecureSettingsProto.Assist.TOUCH_GESTURE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED,
+ SecureSettingsProto.Assist.LONG_PRESS_HOME_ENABLED);
p.end(assistToken);
final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES);
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 44f52ef..bbf69a9 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -18,32 +18,52 @@
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/quick_qs_status_icons"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/qs_header_top_padding"
- android:paddingBottom="@dimen/qs_header_bottom_padding"
- android:layout_below="@id/quick_status_bar_system_icons"
+ android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
- android:minHeight="20dp"
+ android:minHeight="48dp"
android:clickable="false"
android:focusable="true"
android:theme="@style/QSHeaderTheme">
- <com.android.systemui.statusbar.policy.DateView
- android:id="@+id/date"
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:gravity="center_vertical"
+ android:layout_height="match_parent"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:gravity="center_vertical|start"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:singleLine="true"
- android:textAppearance="@style/TextAppearance.QS.Status"
- systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
+
+ <View
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ />
+
+ <!-- Will hold security footer in landscape with media -->
+ <FrameLayout
+ android:id="@+id/header_text_container"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:gravity="center"
+ />
+
+ <include layout="@layout/qs_carrier_group"
+ android:id="@+id/carrier_group"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minHeight="48dp"
+ android:layout_gravity="end|center_vertical"
+ android:focusable="false"/>
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_weight="1"
android:paddingEnd="@dimen/signal_cluster_battery_padding" />
<com.android.systemui.BatteryMeterView
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
deleted file mode 100644
index fb82304..0000000
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 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
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/header_text_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_header_tooltip_height"
- android:layout_below="@id/quick_status_bar_system_icons"
- android:visibility="invisible"
- android:theme="@style/QSHeaderTheme"
- android:forceHasOverlappingRendering="false">
-
- <com.android.systemui.qs.QSHeaderInfoLayout
- android:id="@+id/status_container"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:id = "@+id/alarm_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:focusable="true"
- android:clickable="true">
-
- <ImageView
- android:id="@+id/next_alarm_icon"
- android:layout_width="@dimen/qs_header_alarm_icon_size"
- android:layout_height="@dimen/qs_header_alarm_icon_size"
- android:src="@drawable/ic_alarm"
- android:contentDescription="@string/accessibility_quick_settings_alarm_set"
- android:visibility="gone"/>
-
- <com.android.systemui.util.AutoMarqueeTextView
- android:id="@+id/next_alarm_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
- android:textAppearance="@style/TextAppearance.QS.Status"
- android:visibility="gone"/>
- </LinearLayout>
-
- <View
- android:id="@+id/status_separator"
- android:layout_width="@dimen/qs_header_separator_width"
- android:layout_height="match_parent"
- android:visibility="gone"/>
-
- <LinearLayout
- android:id = "@+id/ringer_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:focusable="true"
- android:clickable="true">
-
- <ImageView
- android:id="@+id/ringer_mode_icon"
- android:layout_width="@dimen/qs_header_alarm_icon_size"
- android:layout_height="@dimen/qs_header_alarm_icon_size"
- android:visibility="gone"/>
-
- <com.android.systemui.util.AutoMarqueeTextView
- android:id="@+id/ringer_mode_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
- android:textAppearance="@style/TextAppearance.QS.Status"
- android:visibility="gone"/>
- </LinearLayout>
- </com.android.systemui.qs.QSHeaderInfoLayout>
-
- <include layout="@layout/qs_carrier_group"
- android:id="@+id/carrier_group"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="@dimen/qs_status_separator"
- android:layout_gravity="end|center_vertical"
- android:focusable="false"/>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 059bda3..5bf6919 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -32,26 +32,32 @@
android:paddingStart="0dp"
android:elevation="4dp" >
- <!-- The clock -->
- <include layout="@layout/quick_status_bar_header_system_icons" />
+ <!-- Date and privacy. Only visible in QS -->
+ <include layout="@layout/quick_status_bar_header_date_privacy"/>
- <!-- Status icons within the panel itself (and not in the top-most status bar) -->
- <include layout="@layout/quick_qs_status_icons" />
-
- <!-- Layout containing tooltips, alarm text, etc. -->
- <include layout="@layout/quick_settings_header_info" />
+ <RelativeLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+ <!-- Time, icons and Carrier (only in QS) -->
+ <include layout="@layout/quick_qs_status_icons"/>
<com.android.systemui.qs.QuickQSPanel
android:id="@+id/quick_qs_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/quick_qs_status_icons"
- android:accessibilityTraversalAfter="@+id/date_time_group"
+ android:layout_marginTop="8dp"
+ android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
android:accessibilityTraversalBefore="@id/expand_indicator"
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
android:paddingBottom="10dp"
android:importantForAccessibility="yes" />
+ </RelativeLayout>
</com.android.systemui.qs.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
similarity index 72%
rename from packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
rename to packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index f663ab4e..22cf2cb 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -17,33 +17,35 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/quick_status_bar_system_icons"
+ android:id="@+id/quick_status_bar_date_privacy"
android:layout_width="match_parent"
android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
+ android:layout_gravity="top"
android:orientation="horizontal"
android:clickable="true"
- android:paddingTop="@dimen/status_bar_padding_top" >
+ android:paddingTop="@dimen/status_bar_padding_top"
+ android:minHeight="48dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
+ android:minHeight="48dp"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|start" >
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:minWidth="48dp"
- android:gravity="center_vertical|start"
- android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
+ <com.android.systemui.statusbar.policy.DateView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
</LinearLayout>
<android.widget.Space
@@ -56,6 +58,7 @@
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
+ android:minHeight="48dp"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|end" >
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 9d43e0c..c8dfde1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -209,6 +209,8 @@
private @TransitionMode int mNavigationBarMode;
private ContentResolver mContentResolver;
private boolean mAssistantAvailable;
+ private boolean mLongPressHomeEnabled;
+ private boolean mAssistantTouchGestureEnabled;
private int mDisabledFlags1;
private int mDisabledFlags2;
@@ -309,7 +311,7 @@
// Send the assistant availability upon connection
if (isConnected) {
- sendAssistantAvailability(mAssistantAvailable);
+ updateAssistantEntrypoints();
}
}
@@ -404,12 +406,7 @@
new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri) {
- boolean available = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
- if (mAssistantAvailable != available) {
- sendAssistantAvailability(available);
- mAssistantAvailable = available;
- }
+ updateAssistantEntrypoints();
}
};
@@ -531,6 +528,13 @@
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ updateAssistantEntrypoints();
if (savedState != null) {
mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
@@ -823,7 +827,7 @@
|| mNavigationBarView.getHomeButton().getCurrentView() == null) {
return;
}
- if (mHomeButtonLongPressDurationMs.isPresent()) {
+ if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) {
mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false);
mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
mNavigationBarView.getHomeButton().setOnLongClickListener(null);
@@ -845,6 +849,8 @@
pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
pw.println(" mCurrentRotation=" + mCurrentRotation);
pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
+ pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled);
+ pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
if (mNavigationBarView != null) {
pw.println(" mNavigationBarWindowState="
@@ -1206,9 +1212,11 @@
return true;
}
}
- mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
- mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
- });
+ if (mLongPressHomeEnabled) {
+ mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
+ mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
+ });
+ }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
@@ -1480,15 +1488,23 @@
| (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
}
- private void sendAssistantAvailability(boolean available) {
+ private void updateAssistantEntrypoints() {
+ mAssistantAvailable = mAssistManagerLazy.get()
+ .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ mLongPressHomeEnabled = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, 1) != 0;
+ mAssistantTouchGestureEnabled = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, 1) != 0;
if (mOverviewProxyService.getProxy() != null) {
try {
- mOverviewProxyService.getProxy().onAssistantAvailable(available
+ mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
+ && mAssistantTouchGestureEnabled
&& QuickStepContract.isGesturalMode(mNavBarMode));
} catch (RemoteException e) {
Log.w(TAG, "Unable to send assistant availability data to launcher");
}
}
+ reconfigureHomeLongClick();
}
// ----- Methods that DisplayNavigationBarController talks to -----
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index b8823e1..ee0b0c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -65,12 +65,14 @@
private final QuickQSPanel mQuickQsPanel;
private final QSPanelController mQsPanelController;
private final QuickQSPanelController mQuickQSPanelController;
+ private final QuickStatusBarHeader mQuickStatusBarHeader;
private final QSSecurityFooter mSecurityFooter;
private final QS mQs;
private PagedTileLayout mPagedLayout;
private boolean mOnFirstPage = true;
+ private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private TouchAnimator mFirstPageAnimator;
private TouchAnimator mFirstPageDelayedAnimator;
private TouchAnimator mTranslationXAnimator;
@@ -98,19 +100,22 @@
private final FeatureFlags mFeatureFlags;
@Inject
- public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanelController qsPanelController,
+ public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader,
+ QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags, QSExpansionPathInterpolator qsExpansionPathInterpolator) {
mQs = qs;
mQuickQsPanel = quickPanel;
mQsPanelController = qsPanelController;
mQuickQSPanelController = quickQSPanelController;
+ mQuickStatusBarHeader = quickStatusBarHeader;
mSecurityFooter = securityFooter;
mHost = qsTileHost;
mExecutor = executor;
mTunerService = tunerService;
mFeatureFlags = featureFlags;
+ mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mHost.addCallback(this);
mQsPanelController.addOnAttachStateChangeListener(this);
qs.getView().addOnLayoutChangeListener(this);
@@ -252,7 +257,9 @@
if (count < tileLayout.getNumVisibleTiles()) {
getRelativePosition(loc1, quickTileView, view);
getRelativePosition(loc2, tileView, view);
- int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0;
+ int yOffset = qsSideLabelsEnabled
+ ? loc2[1] - loc1[1]
+ : mQuickStatusBarHeader.getOffsetTranslation();
// Move the quick tile right from its location to the new one.
View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView;
translationXBuilder.addFloat(v, "translationX", 0, xDiff);
@@ -266,8 +273,14 @@
translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
if (qsSideLabelsEnabled) {
- translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset);
- translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0);
+ // Offset the translation animation on the views
+ // (that goes from 0 to getOffsetTranslation)
+ int offsetWithQSBHTranslation =
+ yOffset - mQuickStatusBarHeader.getOffsetTranslation();
+ translationYBuilder.addFloat(quickTileView, "translationY", 0,
+ offsetWithQSBHTranslation);
+ translationYBuilder.addFloat(tileView, "translationY",
+ -offsetWithQSBHTranslation, 0);
if (mQQSTileHeightAnimator == null) {
mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
@@ -375,22 +388,23 @@
}
float px = 0;
- float py = 1;
if (tiles.size() <= 3) {
px = 1;
} else if (tiles.size() <= 6) {
px = .4f;
}
- PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, px, py);
- translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator());
- translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
+ mQSExpansionPathInterpolator.setControlX2(px);
+ translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
+ translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
mTranslationXAnimator = translationXBuilder.build();
mTranslationYAnimator = translationYBuilder.build();
if (mQQSTileHeightAnimator != null) {
- mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+ mQQSTileHeightAnimator.setInterpolator(
+ mQSExpansionPathInterpolator.getYInterpolator());
}
if (mOtherTilesExpandAnimator != null) {
- mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+ mOtherTilesExpandAnimator.setInterpolator(
+ mQSExpansionPathInterpolator.getYInterpolator());
}
}
mNonfirstPageAnimator = new TouchAnimator.Builder()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt b/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt
new file mode 100644
index 0000000..d351b89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import android.view.animation.Interpolator
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class QSExpansionPathInterpolator @Inject constructor() {
+
+ private var pathInterpolatorBuilder = PathInterpolatorBuilder(0f, 0f, 0f, 1f)
+ private var lastX = 0f
+ val xInterpolator: Interpolator
+ get() = pathInterpolatorBuilder.xInterpolator
+
+ val yInterpolator: Interpolator
+ get() = pathInterpolatorBuilder.yInterpolator
+
+ fun setControlX2(value: Float) {
+ if (value != lastX) {
+ lastX = value
+ pathInterpolatorBuilder = PathInterpolatorBuilder(0f, 0f, lastX, 1f)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
deleted file mode 100644
index c654621..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2019 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.qs
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import android.widget.FrameLayout
-import com.android.systemui.R
-
-/**
- * Container for the Next Alarm and Ringer status texts in [QuickStatusBarHeader].
- *
- * If both elements are visible, it splits the available space according to the following rules:
- * * If both views add up to less than the total space, they take all the space they need.
- * * If both views are larger than half the space, each view takes half the space.
- * * Otherwise, the smaller view takes the space it needs and the larger one takes all remaining
- * space.
- */
-class QSHeaderInfoLayout @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0,
- defStyleRes: Int = 0
-) : FrameLayout(context, attrs, defStyle, defStyleRes) {
-
- private lateinit var alarmContainer: View
- private lateinit var ringerContainer: View
- private lateinit var statusSeparator: View
- private val location = Location(0, 0)
-
- override fun onFinishInflate() {
- super.onFinishInflate()
- alarmContainer = findViewById(R.id.alarm_container)
- ringerContainer = findViewById(R.id.ringer_container)
- statusSeparator = findViewById(R.id.status_separator)
- }
-
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
- // At most one view is there
- if (statusSeparator.visibility == View.GONE) super.onLayout(changed, l, t, r, b)
- else {
- val layoutRTL = isLayoutRtl
- val width = r - l
- val height = b - t
- var offset = 0
-
- offset += alarmContainer.layoutView(width, height, offset, layoutRTL)
- offset += statusSeparator.layoutView(width, height, offset, layoutRTL)
- ringerContainer.layoutView(width, height, offset, layoutRTL)
- }
- }
-
- private fun View.layoutView(pWidth: Int, pHeight: Int, offset: Int, RTL: Boolean): Int {
- location.setLocationFromOffset(pWidth, offset, this.measuredWidth, RTL)
- layout(location.left, 0, location.right, pHeight)
- return this.measuredWidth
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(
- MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST),
- heightMeasureSpec)
- val width = MeasureSpec.getSize(widthMeasureSpec)
- // Once we measure the views, using as much space as they need, we need to remeasure them
- // assigning them their final width. This is because TextViews decide whether to MARQUEE
- // after onMeasure.
- if (statusSeparator.visibility != View.GONE) {
- val alarmWidth = alarmContainer.measuredWidth
- val separatorWidth = statusSeparator.measuredWidth
- val ringerWidth = ringerContainer.measuredWidth
- val availableSpace = MeasureSpec.getSize(width) - separatorWidth
- if (alarmWidth < availableSpace / 2) {
- measureChild(
- ringerContainer,
- MeasureSpec.makeMeasureSpec(
- Math.min(ringerWidth, availableSpace - alarmWidth),
- MeasureSpec.AT_MOST),
- heightMeasureSpec)
- } else if (ringerWidth < availableSpace / 2) {
- measureChild(alarmContainer,
- MeasureSpec.makeMeasureSpec(
- Math.min(alarmWidth, availableSpace - ringerWidth),
- MeasureSpec.AT_MOST),
- heightMeasureSpec)
- } else {
- measureChild(
- alarmContainer,
- MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST),
- heightMeasureSpec)
- measureChild(
- ringerContainer,
- MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST),
- heightMeasureSpec)
- }
- }
- setMeasuredDimension(width, measuredHeight)
- }
-
- private data class Location(var left: Int, var right: Int) {
- /**
- * Sets the [left] and [right] with the correct values for laying out the child, respecting
- * RTL. Only set the variable through here to prevent concurrency issues.
- * This is done to prevent allocation of [Pair] in [onLayout].
- */
- fun setLocationFromOffset(parentWidth: Int, offset: Int, width: Int, RTL: Boolean) {
- if (RTL) {
- left = parentWidth - offset - width
- right = parentWidth - offset
- } else {
- left = offset
- right = offset + width
- }
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index c794a21..95f7e20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -391,20 +391,13 @@
index++;
if (mSecurityFooter != null) {
- LinearLayout.LayoutParams layoutParams =
- (LayoutParams) mSecurityFooter.getLayoutParams();
if (mUsingHorizontalLayout && mHeaderContainer != null) {
// Adding the security view to the header, that enables us to avoid scrolling
- layoutParams.width = 0;
- layoutParams.weight = 1.6f;
- switchToParent(mSecurityFooter, mHeaderContainer, 1 /* always in second place */);
+ switchToParent(mSecurityFooter, mHeaderContainer, 0);
} else {
- layoutParams.width = LayoutParams.WRAP_CONTENT;
- layoutParams.weight = 0;
switchToParent(mSecurityFooter, parent, index);
index++;
}
- mSecurityFooter.setLayoutParams(layoutParams);
}
if (mFooter != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a0bf584..82ae2dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,15 +17,14 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.annotation.ColorInt;
-import android.app.AlarmManager.AlarmClockInfo;
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
+
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.media.AudioManager;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.Pair;
@@ -34,66 +33,46 @@
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
import android.widget.Space;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.qs.QSDetail.Callback;
+import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
+import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
-import java.util.Locale;
-import java.util.Objects;
-
/**
- * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
- * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
- * contents.
+ * View that contains the top-most bits of the QS panel (primarily the status bar with date, time,
+ * battery, carrier info and privacy icons) and also contains the {@link QuickQSPanel}.
*/
-public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwner {
-
- public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
+public class QuickStatusBarHeader extends FrameLayout {
private boolean mExpanded;
private boolean mQsDisabled;
+ private TouchAnimator mAlphaAnimator;
+ private TouchAnimator mTranslationAnimator;
+
protected QuickQSPanel mHeaderQsPanel;
- private TouchAnimator mStatusIconsAlphaAnimator;
- private TouchAnimator mHeaderTextContainerAlphaAnimator;
+ private View mDatePrivacyView;
+ private View mClockIconsView;
+ private View mContainer;
- private View mSystemIconsView;
- private View mQuickQsStatusIcons;
- private View mHeaderTextContainerView;
-
- private ImageView mNextAlarmIcon;
- /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
- private TextView mNextAlarmTextView;
- private View mNextAlarmContainer;
- private View mStatusSeparator;
- private ImageView mRingerModeIcon;
- private TextView mRingerModeTextView;
- private View mRingerContainer;
+ private View mQSCarriers;
private Clock mClockView;
- private OngoingPrivacyChip mPrivacyChip;
private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
- private TintedIconManager mTintedIconManager;
+ private StatusIconContainer mIconContainer;
- // Used for RingerModeTracker
- private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
+ private TintedIconManager mTintedIconManager;
+ private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private int mStatusBarPaddingTop = 0;
private int mRoundedCornerPadding = 0;
@@ -102,12 +81,26 @@
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
- private float mExpandedHeaderAlpha = 1.0f;
+ private float mClockIconsAlpha = 1.0f;
+ private float mDatePrivacyAlpha = 1.0f;
private float mKeyguardExpansionFraction;
private int mTextColorPrimary = Color.TRANSPARENT;
+ private int mTopViewMeasureHeight;
+
+ private final String mMobileSlotName;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
+ mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile);
+ }
+
+ /**
+ * How much the view containing the clock and QQS will translate down when QS is fully expanded.
+ *
+ * This matches the measured height of the view containing the date and privacy icons.
+ */
+ public int getOffsetTranslation() {
+ return mTopViewMeasureHeight;
}
@Override
@@ -115,19 +108,12 @@
super.onFinishInflate();
mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
- mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
- mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
+ mDatePrivacyView = findViewById(R.id.quick_status_bar_date_privacy);
+ mClockIconsView = findViewById(R.id.quick_qs_status_icons);
+ mQSCarriers = findViewById(R.id.carrier_group);
+ mContainer = findViewById(R.id.container);
+ mIconContainer = findViewById(R.id.statusIcons);
- // Views corresponding to the header info section (e.g. ringer and next alarm).
- mHeaderTextContainerView = findViewById(R.id.header_text_container);
- mStatusSeparator = findViewById(R.id.status_separator);
- mNextAlarmIcon = findViewById(R.id.next_alarm_icon);
- mNextAlarmTextView = findViewById(R.id.next_alarm_text);
- mNextAlarmContainer = findViewById(R.id.alarm_container);
- mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
- mRingerModeTextView = findViewById(R.id.ringer_mode_text);
- mRingerContainer = findViewById(R.id.ringer_container);
- mPrivacyChip = findViewById(R.id.privacy_chip);
mClockView = findViewById(R.id.clock);
mSpace = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
@@ -140,79 +126,34 @@
// QS will always show the estimate, and BatteryMeterView handles the case where
// it's unavailable or charging
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
- mRingerModeTextView.setSelected(true);
- mNextAlarmTextView.setSelected(true);
}
- void onAttach(TintedIconManager iconManager) {
+ void onAttach(TintedIconManager iconManager,
+ QSExpansionPathInterpolator qsExpansionPathInterpolator) {
mTintedIconManager = iconManager;
int fillColor = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.textColorPrimary);
// Set the correct tint for the status icons so they contrast
iconManager.setTint(fillColor);
- mNextAlarmIcon.setImageTintList(ColorStateList.valueOf(fillColor));
- mRingerModeIcon.setImageTintList(ColorStateList.valueOf(fillColor));
+
+ mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
+ updateAnimators();
}
public QuickQSPanel getHeaderQsPanel() {
return mHeaderQsPanel;
}
- void updateStatusText(int ringerMode, AlarmClockInfo nextAlarm, boolean zenOverridingRinger,
- boolean use24HourFormat) {
- boolean changed = updateRingerStatus(ringerMode, zenOverridingRinger)
- || updateAlarmStatus(nextAlarm, use24HourFormat);
-
- if (changed) {
- boolean alarmVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
- boolean ringerVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
- mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE
- : View.GONE);
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) {
+ mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight();
+ updateAnimators();
}
}
- private boolean updateRingerStatus(int ringerMode, boolean zenOverridingRinger) {
- boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
- CharSequence originalRingerText = mRingerModeTextView.getText();
-
- boolean ringerVisible = false;
- if (!zenOverridingRinger) {
- if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
- mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
- mRingerModeTextView.setText(R.string.qs_status_phone_vibrate);
- ringerVisible = true;
- } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
- mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
- mRingerModeTextView.setText(R.string.qs_status_phone_muted);
- ringerVisible = true;
- }
- }
- mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
- mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
- mRingerContainer.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
-
- return isOriginalVisible != ringerVisible ||
- !Objects.equals(originalRingerText, mRingerModeTextView.getText());
- }
-
- private boolean updateAlarmStatus(AlarmClockInfo nextAlarm, boolean use24HourFormat) {
- boolean isOriginalVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
- CharSequence originalAlarmText = mNextAlarmTextView.getText();
-
- boolean alarmVisible = false;
- if (nextAlarm != null) {
- alarmVisible = true;
- mNextAlarmTextView.setText(formatNextAlarm(nextAlarm, use24HourFormat));
- }
- mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
- mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
- mNextAlarmContainer.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
-
- return isOriginalVisible != alarmVisible ||
- !Objects.equals(originalAlarmText, mNextAlarmTextView.getText());
- }
-
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -225,40 +166,27 @@
updateResources();
}
- /**
- * The height of QQS should always be the status bar height + 128dp. This is normally easy, but
- * when there is a notch involved the status bar can remain a fixed pixel size.
- */
- private void updateMinimumHeight() {
- int sbHeight = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- int qqsHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_quick_header_panel_height);
-
- setMinimumHeight(sbHeight + qqsHeight);
- }
-
void updateResources() {
Resources resources = mContext.getResources();
- updateMinimumHeight();
mRoundedCornerPadding = resources.getDimensionPixelSize(
R.dimen.rounded_corner_content_padding);
mStatusBarPaddingTop = resources.getDimensionPixelSize(R.dimen.status_bar_padding_top);
- // Update height for a few views, especially due to landscape mode restricting space.
- mHeaderTextContainerView.getLayoutParams().height =
- resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
- mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
-
- mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize(
+ int qsOffsetHeight = resources.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
+
+ mDatePrivacyView.getLayoutParams().height =
+ Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight());
+ mDatePrivacyView.setLayoutParams(mDatePrivacyView.getLayoutParams());
+
+ mClockIconsView.getLayoutParams().height =
+ Math.max(qsOffsetHeight, mClockIconsView.getMinimumHeight());
+ mClockIconsView.setLayoutParams(mClockIconsView.getLayoutParams());
ViewGroup.LayoutParams lp = getLayoutParams();
if (mQsDisabled) {
- lp.height = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height);
+ lp.height = mClockIconsView.getLayoutParams().height;
} else {
lp.height = WRAP_CONTENT;
}
@@ -276,21 +204,45 @@
mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary,
mTextColorPrimary);
}
-
- updateStatusIconAlphaAnimator();
- updateHeaderTextContainerAlphaAnimator();
+ updateHeadersPadding();
+ updateAnimators();
}
- private void updateStatusIconAlphaAnimator() {
- mStatusIconsAlphaAnimator = new TouchAnimator.Builder()
- .addFloat(mQuickQsStatusIcons, "alpha", 1, 0, 0)
+ private void updateAnimators() {
+ updateAlphaAnimator();
+ int offset = mTopViewMeasureHeight;
+
+ mTranslationAnimator = new TouchAnimator.Builder()
+ .addFloat(mContainer, "translationY", 0, offset)
+ .setInterpolator(mQSExpansionPathInterpolator != null
+ ? mQSExpansionPathInterpolator.getYInterpolator()
+ : null)
.build();
}
- private void updateHeaderTextContainerAlphaAnimator() {
- mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
- .addFloat(mHeaderTextContainerView, "alpha", 0, 0, mExpandedHeaderAlpha)
- .build();
+ private void updateAlphaAnimator() {
+ StatusBarMobileView icon =
+ ((StatusBarMobileView) mIconContainer.getViewForSlot(mMobileSlotName));
+ TouchAnimator.Builder builder = new TouchAnimator.Builder()
+ .addFloat(mQSCarriers, "alpha", 0, 1)
+ .addFloat(mDatePrivacyView, "alpha", 0, mDatePrivacyAlpha);
+ if (icon != null) {
+ builder.addFloat(icon, "alpha", 1, 0);
+ builder.setListener(new TouchAnimator.ListenerAdapter() {
+ @Override
+ public void onAnimationAtEnd() {
+ icon.forceHidden(true);
+ icon.setVisibleState(STATE_HIDDEN);
+ }
+
+ @Override
+ public void onAnimationStarted() {
+ icon.forceHidden(false);
+ icon.setVisibleState(STATE_ICON);
+ }
+ });
+ }
+ mAlphaAnimator = builder.build();
}
/** */
@@ -312,25 +264,19 @@
public void setExpansion(boolean forceExpanded, float expansionFraction,
float panelTranslationY) {
final float keyguardExpansionFraction = forceExpanded ? 1f : expansionFraction;
- if (mStatusIconsAlphaAnimator != null) {
- mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
- }
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.setPosition(keyguardExpansionFraction);
+ }
+ if (mTranslationAnimator != null) {
+ mTranslationAnimator.setPosition(keyguardExpansionFraction);
+ }
+ // If forceExpanded (we are opening QS from lockscreen), the animators have been set to
+ // position = 1f.
if (forceExpanded) {
- // If the keyguard is showing, we want to offset the text so that it comes in at the
- // same time as the panel as it slides down.
- mHeaderTextContainerView.setTranslationY(panelTranslationY);
+ setTranslationY(panelTranslationY);
} else {
- mHeaderTextContainerView.setTranslationY(0f);
- }
-
- if (mHeaderTextContainerAlphaAnimator != null) {
- mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
- if (keyguardExpansionFraction > 0) {
- mHeaderTextContainerView.setVisibility(VISIBLE);
- } else {
- mHeaderTextContainerView.setVisibility(INVISIBLE);
- }
+ setTranslationY(0);
}
mKeyguardExpansionFraction = keyguardExpansionFraction;
@@ -341,28 +287,21 @@
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
mHeaderQsPanel.setDisabledByPolicy(disabled);
- mHeaderTextContainerView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
- mQuickQsStatusIcons.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+ mClockIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
updateResources();
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- // Handle padding of the clock
+ // Handle padding of the views
DisplayCutout cutout = insets.getDisplayCutout();
Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
cutout, getDisplay());
Pair<Integer, Integer> padding =
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
cutout, cornerCutoutPadding, -1);
- if (padding == null) {
- mSystemIconsView.setPaddingRelative(
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
- } else {
- mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
-
- }
+ mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
+ mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
boolean cornerCutout = cornerCutoutPadding != null
&& (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
@@ -380,13 +319,13 @@
mCutOutPaddingLeft = padding.first;
mCutOutPaddingRight = padding.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
- updateClockPadding();
+ updateHeadersPadding();
return super.onApplyWindowInsets(insets);
}
- private void updateClockPadding() {
- int clockPaddingLeft = 0;
- int clockPaddingRight = 0;
+ private void updateHeadersPadding() {
+ int paddingLeft = 0;
+ int paddingRight = 0;
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
int leftMargin = lp.leftMargin;
@@ -399,18 +338,22 @@
// if there's a cutout, let's use at least the rounded corner inset
int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding);
int contentMarginLeft = isLayoutRtl() ? mContentMarginEnd : mContentMarginStart;
- clockPaddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0);
+ paddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0);
}
if (mCutOutPaddingRight > 0) {
// if there's a cutout, let's use at least the rounded corner inset
int cutoutPadding = Math.max(mCutOutPaddingRight, mRoundedCornerPadding);
int contentMarginRight = isLayoutRtl() ? mContentMarginStart : mContentMarginEnd;
- clockPaddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0);
+ paddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0);
}
- mSystemIconsView.setPadding(clockPaddingLeft,
+ mDatePrivacyView.setPadding(paddingLeft,
mWaterfallTopInset + mStatusBarPaddingTop,
- clockPaddingRight,
+ paddingRight,
+ 0);
+ mClockIconsView.setPadding(paddingLeft,
+ mWaterfallTopInset + mStatusBarPaddingTop,
+ paddingRight,
0);
}
@@ -422,26 +365,6 @@
mHeaderQsPanel.setCallback(qsPanelCallback);
}
- private String formatNextAlarm(AlarmClockInfo info, boolean use24HourFormat) {
- if (info == null) {
- return "";
- }
- String skeleton = use24HourFormat ? "EHm" : "Ehma";
- String pattern = android.text.format.DateFormat
- .getBestDateTimePattern(Locale.getDefault(), skeleton);
- return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
- }
-
- public static float getColorIntensity(@ColorInt int color) {
- return color == Color.WHITE ? 0 : 1;
- }
-
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycle;
- }
-
/** */
public void setContentMargins(int marginStart, int marginEnd,
QuickQSPanelController quickQSPanelController) {
@@ -459,24 +382,39 @@
view.setLayoutParams(lp);
}
}
- updateClockPadding();
+ updateHeadersPadding();
}
+ /**
+ * When QS is scrolling, mClockIconsAlpha should scroll away and fade out.
+ *
+ * For a given scroll level, this method does the following:
+ * <ol>
+ * <li>Determine the alpha that {@code mClockIconsView} should have when the panel is fully
+ * expanded.</li>
+ * <li>Set the scroll of {@code mClockIconsView} to the same of {@code QSPanel}.</li>
+ * <li>Set the alpha of {@code mClockIconsView} to that determined by the expansion of
+ * the panel, interpolated between 1 (no expansion) and {@code mClockIconsAlpha} (fully
+ * expanded), matching the animator.</li>
+ * </ol>
+ *
+ * @param scrollY the scroll of the QSPanel container
+ */
public void setExpandedScrollAmount(int scrollY) {
// The scrolling of the expanded qs has changed. Since the header text isn't part of it,
// but would overlap content, we're fading it out.
float newAlpha = 1.0f;
- if (mHeaderTextContainerView.getHeight() > 0) {
- newAlpha = MathUtils.map(0, mHeaderTextContainerView.getHeight() / 2.0f, 1.0f, 0.0f,
+ if (mClockIconsView.getHeight() > 0) {
+ newAlpha = MathUtils.map(0, mClockIconsView.getHeight() / 2.0f, 1.0f, 0.0f,
scrollY);
newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha);
}
- mHeaderTextContainerView.setScrollY(scrollY);
- if (newAlpha != mExpandedHeaderAlpha) {
- mExpandedHeaderAlpha = newAlpha;
- mHeaderTextContainerView.setAlpha(MathUtils.lerp(0.0f, mExpandedHeaderAlpha,
+ mClockIconsView.setScrollY(scrollY);
+ if (newAlpha != mClockIconsAlpha) {
+ mClockIconsAlpha = newAlpha;
+ mClockIconsView.setAlpha(MathUtils.lerp(1.0f, mClockIconsAlpha,
mKeyguardExpansionFraction));
- updateHeaderTextContainerAlphaAnimator();
+ updateAlphaAnimator();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index b1689f6..3aafea98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -16,21 +16,13 @@
package com.android.systemui.qs;
-import android.app.AlarmManager.AlarmClockInfo;
import android.content.Intent;
-import android.media.AudioManager;
import android.os.Bundle;
import android.provider.AlarmClock;
-import android.provider.Settings;
-import android.service.notification.ZenModeConfig;
-import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
@@ -47,15 +39,9 @@
import com.android.systemui.privacy.logging.PrivacyLogger;
import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeController.Callback;
-import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.ViewController;
import java.util.ArrayList;
@@ -70,75 +56,29 @@
class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
private static final String TAG = "QuickStatusBarHeader";
- private final ZenModeController mZenModeController;
- private final NextAlarmController mNextAlarmController;
private final PrivacyItemController mPrivacyItemController;
- private final RingerModeTracker mRingerModeTracker;
private final ActivityStarter mActivityStarter;
private final UiEventLogger mUiEventLogger;
private final QSCarrierGroupController mQSCarrierGroupController;
private final QuickQSPanelController mHeaderQsPanelController;
- private final LifecycleRegistry mLifecycle;
private final OngoingPrivacyChip mPrivacyChip;
private final Clock mClockView;
- private final View mNextAlarmContainer;
- private final View mRingerContainer;
- private final QSTileHost mQSTileHost;
private final StatusBarIconController mStatusBarIconController;
private final DemoModeController mDemoModeController;
- private final UserTracker mUserTracker;
private final StatusIconContainer mIconContainer;
private final StatusBarIconController.TintedIconManager mIconManager;
private final DemoMode mDemoModeReceiver;
private final PrivacyLogger mPrivacyLogger;
private final PrivacyDialogController mPrivacyDialogController;
+ private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private boolean mListening;
- private AlarmClockInfo mNextAlarm;
private boolean mMicCameraIndicatorsEnabled;
private boolean mLocationIndicatorsEnabled;
private boolean mPrivacyChipLogged;
private SysuiColorExtractor mColorExtractor;
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
- private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
-
- private final ZenModeController.Callback mZenModeControllerCallback = new Callback() {
- @Override
- public void onZenChanged(int zen) {
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- }
-
- @Override
- public void onConfigChanged(ZenModeConfig config) {
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- }
- };
-
- private boolean use24HourFormat() {
- return android.text.format.DateFormat.is24HourFormat(
- mView.getContext(), mUserTracker.getUserId());
-
- }
-
- private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() {
- @Override
- public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
- mNextAlarm = nextAlarm;
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- }
- };
-
- private final LifecycleOwner mLifecycleOwner = new LifecycleOwner() {
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycle;
- }
- };
private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
@Override
@@ -176,63 +116,43 @@
if (v == mClockView) {
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
- } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) {
- if (mNextAlarm.getShowIntent() != null) {
- mActivityStarter.postStartActivityDismissingKeyguard(
- mNextAlarm.getShowIntent());
- } else {
- Log.d(TAG, "No PendingIntent for next alarm. Using default intent");
- mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
- AlarmClock.ACTION_SHOW_ALARMS), 0);
- }
} else if (v == mPrivacyChip) {
// If the privacy chip is visible, it means there were some indicators
mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
mPrivacyDialogController.showDialog(getContext());
- } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
- mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
- Settings.ACTION_SOUND_SETTINGS), 0);
}
}
};
@Inject
QuickStatusBarHeaderController(QuickStatusBarHeader view,
- ZenModeController zenModeController, NextAlarmController nextAlarmController,
- PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker,
+ PrivacyItemController privacyItemController,
ActivityStarter activityStarter, UiEventLogger uiEventLogger,
- QSTileHost qsTileHost, StatusBarIconController statusBarIconController,
+ StatusBarIconController statusBarIconController,
DemoModeController demoModeController,
- UserTracker userTracker, QuickQSPanelController quickQSPanelController,
+ QuickQSPanelController quickQSPanelController,
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
PrivacyLogger privacyLogger,
SysuiColorExtractor colorExtractor,
- PrivacyDialogController privacyDialogController) {
+ PrivacyDialogController privacyDialogController,
+ QSExpansionPathInterpolator qsExpansionPathInterpolator) {
super(view);
- mZenModeController = zenModeController;
- mNextAlarmController = nextAlarmController;
mPrivacyItemController = privacyItemController;
- mRingerModeTracker = ringerModeTracker;
mActivityStarter = activityStarter;
mUiEventLogger = uiEventLogger;
- mQSTileHost = qsTileHost;
mStatusBarIconController = statusBarIconController;
mDemoModeController = demoModeController;
- mUserTracker = userTracker;
- mLifecycle = new LifecycleRegistry(mLifecycleOwner);
mHeaderQsPanelController = quickQSPanelController;
mPrivacyLogger = privacyLogger;
mPrivacyDialogController = privacyDialogController;
+ mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
.build();
-
mPrivacyChip = mView.findViewById(R.id.privacy_chip);
- mNextAlarmContainer = mView.findViewById(R.id.alarm_container);
mClockView = mView.findViewById(R.id.clock);
- mRingerContainer = mView.findViewById(R.id.ringer_container);
mIconContainer = mView.findViewById(R.id.statusIcons);
mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer);
@@ -247,15 +167,7 @@
@Override
protected void onViewAttached() {
- mRingerModeTracker.getRingerModeInternal().observe(mLifecycleOwner, ringer -> {
- mRingerMode = ringer;
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- });
-
mClockView.setOnClickListener(mOnClickListener);
- mNextAlarmContainer.setOnClickListener(mOnClickListener);
- mRingerContainer.setOnClickListener(mOnClickListener);
mPrivacyChip.setOnClickListener(mOnClickListener);
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
@@ -268,18 +180,15 @@
setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
- mView.onAttach(mIconManager);
+ mView.onAttach(mIconManager, mQSExpansionPathInterpolator);
mDemoModeController.addCallback(mDemoModeReceiver);
}
@Override
protected void onViewDetached() {
- mRingerModeTracker.getRingerModeInternal().removeObservers(mLifecycleOwner);
mClockView.setOnClickListener(null);
mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
- mNextAlarmContainer.setOnClickListener(null);
- mRingerContainer.setOnClickListener(null);
mPrivacyChip.setOnClickListener(null);
mStatusBarIconController.removeIconGroup(mIconManager);
mDemoModeController.removeCallback(mDemoModeReceiver);
@@ -304,17 +213,11 @@
}
if (listening) {
- mZenModeController.addCallback(mZenModeControllerCallback);
- mNextAlarmController.addCallback(mNextAlarmChangeCallback);
- mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
// Get the most up to date info
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
mPrivacyItemController.addCallback(mPICCallback);
} else {
- mZenModeController.removeCallback(mZenModeControllerCallback);
- mNextAlarmController.removeCallback(mNextAlarmChangeCallback);
- mLifecycle.setCurrentState(Lifecycle.State.CREATED);
mPrivacyItemController.removeCallback(mPICCallback);
mPrivacyChipLogged = false;
}
@@ -357,11 +260,6 @@
return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
}
- private boolean isZenOverridingRinger() {
- return ZenModeConfig.isZenOverridingRinger(mZenModeController.getZen(),
- mZenModeController.getConsolidatedPolicy());
- }
-
public void setContentMargins(int contentPaddingStart, int contentPaddingEnd) {
mView.setContentMargins(contentPaddingStart, contentPaddingEnd, mHeaderQsPanelController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 0ebadfd..d1deeca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.util.AttributeSet
+import com.android.systemui.R
open class SideLabelTileLayout(
context: Context,
@@ -26,7 +27,7 @@
override fun updateResources(): Boolean {
return super.updateResources().also {
- mMaxAllowedRows = 4
+ mMaxAllowedRows = context.resources.getInteger(R.integer.quick_settings_max_rows)
}
}
@@ -44,4 +45,19 @@
val row = index / mColumns
return getRowTop(row)
}
+
+ override fun updateMaxRows(allowedHeight: Int, tilesCount: Int): Boolean {
+ val previousRows = mRows
+ mRows = mMaxAllowedRows
+ // We want at most mMaxAllowedRows, but it could be that we don't have enough tiles to fit
+ // that many rows. In that case, we want
+ // `tilesCount = (mRows - 1) * mColumns + X`
+ // where X is some remainder between 1 and `mColumns - 1`
+ // Adding `mColumns - 1` will guarantee that the final value F will satisfy
+ // `mRows * mColumns <= F < (mRows + 1) * mColumns
+ if (mRows > (tilesCount + mColumns - 1) / mColumns) {
+ mRows = (tilesCount + mColumns - 1) / mColumns
+ }
+ return previousRows != mRows
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index f9d1def..a17aeba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -56,7 +56,6 @@
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
-import com.android.systemui.Prefs;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
@@ -67,7 +66,6 @@
import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSEvent;
import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.logging.QSLogger;
import java.io.FileDescriptor;
@@ -300,11 +298,6 @@
getInstanceId());
mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
mHandler.sendEmptyMessage(H.LONG_CLICK);
-
- Prefs.putInt(
- mContext,
- Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
- QuickStatusBarHeader.MAX_TOOLTIP_SHOWN_COUNT);
}
public LogMaker populate(LogMaker logMaker) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 138c811..ab17ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -59,6 +59,7 @@
private View mMobileRoamingSpace;
private int mVisibleState = -1;
private DualToneHandler mDualToneHandler;
+ private boolean mForceHidden;
public static StatusBarMobileView fromContext(Context context, String slot) {
LayoutInflater inflater = LayoutInflater.from(context);
@@ -150,7 +151,7 @@
private void initViewState() {
setContentDescription(mState.contentDescription);
- if (!mState.visible) {
+ if (!mState.visible || mForceHidden) {
mMobileGroup.setVisibility(View.GONE);
} else {
mMobileGroup.setVisibility(View.VISIBLE);
@@ -176,8 +177,9 @@
boolean needsLayout = false;
setContentDescription(state.contentDescription);
- if (mState.visible != state.visible) {
- mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
+ int newVisibility = state.visible && !mForceHidden ? View.VISIBLE : View.GONE;
+ if (newVisibility != mMobileGroup.getVisibility()) {
+ mMobileGroup.setVisibility(newVisibility);
needsLayout = true;
}
if (mState.strengthId != state.strengthId) {
@@ -252,7 +254,7 @@
@Override
public boolean isIconVisible() {
- return mState.visible;
+ return mState.visible && !mForceHidden;
}
@Override
@@ -279,6 +281,23 @@
}
}
+ /**
+ * Forces the state to be hidden (views will be GONE) and if necessary updates the layout.
+ *
+ * Makes sure that the {@link StatusBarIconController} cannot make it visible while this flag
+ * is enabled.
+ * @param forceHidden {@code true} if the icon should be GONE in its view regardless of its
+ * state.
+ * {@code false} if the icon should show as determined by its controller.
+ */
+ public void forceHidden(boolean forceHidden) {
+ if (mForceHidden != forceHidden) {
+ mForceHidden = forceHidden;
+ updateState(mState);
+ requestLayout();
+ }
+ }
+
@Override
public int getVisibleState() {
return mVisibleState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index c6f7983..779ba1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -33,6 +33,8 @@
import android.view.WindowInsets;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+
import com.android.systemui.util.leak.RotationUtils;
/**
@@ -91,6 +93,7 @@
* @param roundedCornerContentPadding
* @return
*/
+ @NonNull
public static Pair<Integer, Integer> paddingNeededForCutoutAndRoundedCorner(
DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding,
int roundedCornerContentPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index f65f97a..64a497d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -262,6 +262,25 @@
}
/**
+ * Returns the view corresponding to a particular slot.
+ *
+ * Use it solely to manipulate how it is presented.
+ * @param slot name of the slot to find. Names are defined in
+ * {@link com.android.internal.R.config_statusBarIcons}
+ * @return a view for the slot if this container has it, else {@code null}
+ */
+ public View getViewForSlot(String slot) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof StatusIconDisplayable
+ && ((StatusIconDisplayable) child).getSlot().equals(slot)) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
* Layout is happening from end -> start
*/
private void calculateIconTranslations() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 6e78059..5a78ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -921,13 +921,18 @@
if (clip.getItemCount() > 1
|| description.getMimeTypeCount() < 1
|| remainingItems != null) {
- // TODO(b/172363500): Update to loop over all the items
+ // Direct-reply in notifications currently only supports only one uri item
+ // at a time and requires the MIME type to be set.
+ Log.w(TAG, "Invalid payload: " + payload);
return payload;
}
Uri contentUri = clip.getItemAt(0).getUri();
String mimeType = description.getMimeType(0);
Intent dataIntent =
mRemoteInputView.prepareRemoteInputFromData(mimeType, contentUri);
+ // We can release the uri permissions granted to us as soon as we've created the
+ // grant for the target app in the call above.
+ payload.releasePermissions();
mRemoteInputView.sendRemoteInput(dataIntent);
}
return remainingItems;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 3595095..3c1b36e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -21,9 +21,9 @@
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
@@ -32,16 +32,12 @@
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.policy.Clock
-import com.android.systemui.statusbar.policy.NextAlarmController
-import com.android.systemui.util.RingerModeTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
-import com.android.systemui.utils.leaks.FakeZenModeController
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -61,26 +57,16 @@
@Mock
private lateinit var view: QuickStatusBarHeader
@Mock
- private lateinit var zenModeController: FakeZenModeController
- @Mock
- private lateinit var nextAlarmController: NextAlarmController
- @Mock
private lateinit var privacyItemController: PrivacyItemController
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private lateinit var ringerModeTracker: RingerModeTracker
@Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var uiEventLogger: UiEventLogger
@Mock
- private lateinit var qsTileHost: QSTileHost
- @Mock
private lateinit var statusBarIconController: StatusBarIconController
@Mock
private lateinit var demoModeController: DemoModeController
@Mock
- private lateinit var userTracker: UserTracker
- @Mock
private lateinit var quickQSPanelController: QuickQSPanelController
@Mock(answer = Answers.RETURNS_SELF)
private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
@@ -105,6 +91,8 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var context: Context
+ private val qsExpansionPathInterpolator = QSExpansionPathInterpolator()
+
private lateinit var controller: QuickStatusBarHeaderController
@Before
@@ -119,21 +107,17 @@
controller = QuickStatusBarHeaderController(
view,
- zenModeController,
- nextAlarmController,
privacyItemController,
- ringerModeTracker,
activityStarter,
uiEventLogger,
- qsTileHost,
statusBarIconController,
demoModeController,
- userTracker,
quickQSPanelController,
qsCarrierGroupControllerBuilder,
privacyLogger,
colorExtractor,
- privacyDialogController
+ privacyDialogController,
+ qsExpansionPathInterpolator
)
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 641b38d..1e3f12a 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -158,7 +158,7 @@
"android.hardware.power.stats-V1-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
- "dnsresolver_aidl_interface-V7-java",
+ "dnsresolver_aidl_interface-V8-java",
"icu4j_calendar_astronomer",
"netd-client",
"overlayable_policy_aidl-java",
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 56ad01b..cb8541e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -874,6 +874,10 @@
private void cleanupHandlerThreadAfterStop() {
setProximitySensorEnabled(false);
mHandler.removeCallbacksAndMessages(null);
+ if (mUnfinishedBusiness) {
+ mCallbacks.releaseSuspendBlocker();
+ mUnfinishedBusiness = false;
+ }
if (mPowerState != null) {
mPowerState.stop();
mPowerState = null;
@@ -1701,12 +1705,7 @@
}
}
- private final Runnable mCleanListener = new Runnable() {
- @Override
- public void run() {
- sendUpdatePowerState();
- }
- };
+ private final Runnable mCleanListener = this::sendUpdatePowerState;
private void setProximitySensorEnabled(boolean enable) {
if (enable) {
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 1262dee..5a8af45 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -17,7 +17,9 @@
package com.android.server.wm;
import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH;
+import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
@@ -34,6 +36,9 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -49,15 +54,22 @@
import android.service.displayhash.DisplayHashParams;
import android.service.displayhash.DisplayHasherService;
import android.service.displayhash.IDisplayHasherService;
+import android.util.AttributeSet;
import android.util.Size;
import android.util.Slog;
+import android.util.Xml;
import android.view.MagnificationSpec;
import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -102,6 +114,41 @@
private final Matrix mTmpMatrix = new Matrix();
private final RectF mTmpRectF = new RectF();
+ /**
+ * Lock used when retrieving xml metadata. Lock when retrieving the xml data the first time
+ * since it will be cached after that. Check if {@link #mParsedXml} is set to determine if the
+ * metadata needs to retrieved.
+ */
+ private final Object mParseXmlLock = new Object();
+
+ /**
+ * Flag whether the xml metadata has been retrieved and parsed. Once this is set to true,
+ * there's no need to request metadata again.
+ */
+ @GuardedBy("mParseXmlLock")
+ private boolean mParsedXml;
+
+ /**
+ * Specified throttle time in milliseconds. Don't allow an app to generate a display hash more
+ * than once per throttleTime
+ */
+ private int mThrottleDurationMillis = 0;
+
+ /**
+ * The last time an app requested to generate a display hash in System time.
+ */
+ private long mLastRequestTimeMs;
+
+ /**
+ * The last uid that requested to generate a hash.
+ */
+ private int mLastRequestUid;
+
+ /**
+ * Only used for testing. Throttling should always be enabled unless running tests
+ */
+ private boolean mDisplayHashThrottlingEnabled = true;
+
private interface Command {
void run(IDisplayHasherService service) throws RemoteException;
}
@@ -131,6 +178,10 @@
return results.getParcelable(EXTRA_VERIFIED_DISPLAY_HASH);
}
+ void setDisplayHashThrottlingEnabled(boolean enable) {
+ mDisplayHashThrottlingEnabled = enable;
+ }
+
private void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
String hashAlgorithm, RemoteCallback callback) {
connectAndRun(
@@ -138,8 +189,36 @@
callback));
}
+ private boolean allowedToGenerateHash(int uid) {
+ if (!mDisplayHashThrottlingEnabled) {
+ // Always allow to generate the hash. This is used to allow tests to run without
+ // waiting on the designated threshold.
+ return true;
+ }
+
+ long currentTime = System.currentTimeMillis();
+ if (mLastRequestUid != uid) {
+ mLastRequestUid = uid;
+ mLastRequestTimeMs = currentTime;
+ return true;
+ }
+
+ int throttleDurationMs = getThrottleDurationMillis();
+ if (currentTime - mLastRequestTimeMs < throttleDurationMs) {
+ return false;
+ }
+
+ mLastRequestTimeMs = currentTime;
+ return true;
+ }
+
void generateDisplayHash(SurfaceControl.LayerCaptureArgs.Builder args,
- Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback) {
+ Rect boundsInWindow, String hashAlgorithm, int uid, RemoteCallback callback) {
+ if (!allowedToGenerateHash(uid)) {
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS);
+ return;
+ }
+
final Map<String, DisplayHashParams> displayHashAlgorithmsMap = getDisplayHashAlgorithms();
DisplayHashParams displayHashParams = displayHashAlgorithmsMap.get(hashAlgorithm);
if (displayHashParams == null) {
@@ -277,6 +356,64 @@
}
}
+ private int getThrottleDurationMillis() {
+ if (!parseXmlProperties()) {
+ return 0;
+ }
+ return mThrottleDurationMillis;
+ }
+
+ private boolean parseXmlProperties() {
+ // We have a separate lock for the xml parsing since it doesn't need to make the
+ // request through the service connection. Instead, we have a lock to ensure we can
+ // properly cache the xml metadata so we don't need to call into the ExtServices
+ // process for each request.
+ synchronized (mParseXmlLock) {
+ if (mParsedXml) {
+ return true;
+ }
+
+ final ServiceInfo serviceInfo = getServiceInfo();
+ if (serviceInfo == null) return false;
+
+ final PackageManager pm = mContext.getPackageManager();
+
+ XmlResourceParser parser;
+ parser = serviceInfo.loadXmlMetaData(pm, SERVICE_META_DATA);
+ if (parser == null) {
+ return false;
+ }
+
+ Resources res;
+ try {
+ res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while (true) {
+ try {
+ if (!((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG)) {
+ break;
+ }
+ } catch (XmlPullParserException | IOException e) {
+ return false;
+ }
+ }
+
+ TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHasherService);
+ mThrottleDurationMillis = sa.getInt(
+ R.styleable.DisplayHasherService_throttleDurationMillis, 0);
+ sa.recycle();
+ mParsedXml = true;
+ return true;
+ }
+ }
+
/**
* Run a command, starting the service connection if necessary.
*/
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d494b75..a670b39 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8626,6 +8626,14 @@
return mDisplayHashController.verifyDisplayHash(displayHash);
}
+ @Override
+ public void setDisplayHashThrottlingEnabled(boolean enable) {
+ if (!checkCallingPermission(READ_FRAME_BUFFER, "setDisplayHashThrottle()")) {
+ throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+ }
+ mDisplayHashController.setDisplayHashThrottlingEnabled(enable);
+ }
+
void generateDisplayHash(Session session, IWindow window, Rect boundsInWindow,
String hashAlgorithm, RemoteCallback callback) {
final SurfaceControl displaySurfaceControl;
@@ -8639,6 +8647,13 @@
return;
}
+ if (win.mActivityRecord == null || !win.mActivityRecord.isState(
+ Task.ActivityState.RESUMED)) {
+ mDisplayHashController.sendDisplayHashError(callback,
+ DISPLAY_HASH_ERROR_MISSING_WINDOW);
+ return;
+ }
+
DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display");
@@ -8669,8 +8684,8 @@
.setUid(uid)
.setSourceCrop(boundsInDisplay);
- mDisplayHashController.generateDisplayHash(args, boundsInWindow,
- hashAlgorithm, callback);
+ mDisplayHashController.generateDisplayHash(args, boundsInWindow, hashAlgorithm, uid,
+ callback);
}
boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 619a05e..bd7c185 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -34,7 +34,7 @@
wmHelper: WindowManagerStateHelper?
) {
// do nothing (the app is focused automatically)
- waitAndAssertIMEShown(device, wmHelper)
+ waitIMEShown(device, wmHelper)
}
override fun open() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index d8091a9..83fddae 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -51,17 +51,17 @@
"was left in an unknown state (e.g. in split screen)"
}
editText.click()
- waitAndAssertIMEShown(device, wmHelper)
+ waitIMEShown(device, wmHelper)
}
- protected fun waitAndAssertIMEShown(
+ protected fun waitIMEShown(
device: UiDevice,
wmHelper: WindowManagerStateHelper? = null
) {
if (wmHelper == null) {
device.waitForIdle()
} else {
- require(wmHelper.waitImeWindowShown()) { "IME did not appear" }
+ wmHelper.waitImeWindowShown()
}
}
@@ -78,7 +78,7 @@
if (wmHelper == null) {
device.waitForIdle()
} else {
- require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
+ wmHelper.waitImeWindowGone()
}
}
}
\ No newline at end of file
diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
index b62bdbc..5006d53 100644
--- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
+++ b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
@@ -101,7 +101,7 @@
// Check valid customization generates expected array.
val validRes = arrayOf("0,3", "1,0", "4,4")
- val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0)
+ val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0, 0)
val mockContext = getMockedContextWithStringArrayRes(
R.array.config_networkSupportedKeepaliveCount,
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index a447cef..24c60b7 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -42,6 +42,10 @@
using android::idmap2::policy::kPolicyStringToFlag;
namespace aapt {
+namespace {
+constexpr const char* kPublicGroupTag = "public-group";
+constexpr const char* kStagingPublicGroupTag = "staging-public-group";
+} // namespace
constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
@@ -102,6 +106,7 @@
ResourceId id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
+ bool staged_api = false;
bool allow_new = false;
Maybe<OverlayableItem> overlayable_item;
@@ -122,6 +127,7 @@
if (res->visibility_level != Visibility::Level::kUndefined) {
Visibility visibility;
visibility.level = res->visibility_level;
+ visibility.staged_api = res->staged_api;
visibility.source = res->source;
visibility.comment = res->comment;
res_builder.SetVisibility(visibility);
@@ -525,6 +531,7 @@
{"plurals", std::mem_fn(&ResourceParser::ParsePlural)},
{"public", std::mem_fn(&ResourceParser::ParsePublic)},
{"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
+ {"staging-public-group", std::mem_fn(&ResourceParser::ParseStagingPublicGroup)},
{"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
{"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle,
std::placeholders::_2, std::placeholders::_3)},
@@ -653,7 +660,8 @@
const auto bag_iter = elToBagMap.find(resource_type);
if (bag_iter != elToBagMap.end()) {
// Ensure we have a name (unless this is a <public-group> or <overlayable>).
- if (resource_type != "public-group" && resource_type != "overlayable") {
+ if (resource_type != kPublicGroupTag && resource_type != kStagingPublicGroupTag &&
+ resource_type != "overlayable") {
if (!maybe_name) {
diag_->Error(DiagMessage(out_resource->source)
<< "<" << parser->element_name() << "> missing 'name' attribute");
@@ -890,54 +898,45 @@
return true;
}
-bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
- if (options_.visibility) {
- diag_->Error(DiagMessage(out_resource->source)
- << "<public-group> tag not allowed with --visibility flag");
- return false;
- }
-
+template <typename Func>
+bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
+ const char* tag_name, IDiagnostics* diag, Func&& func) {
if (out_resource->config != ConfigDescription::DefaultConfig()) {
- diag_->Warn(DiagMessage(out_resource->source)
- << "ignoring configuration '" << out_resource->config
- << "' for <public-group> tag");
+ diag->Warn(DiagMessage(out_resource->source)
+ << "ignoring configuration '" << out_resource->config << "' for <" << tag_name
+ << "> tag");
}
Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
- diag_->Error(DiagMessage(out_resource->source)
- << "<public-group> must have a 'type' attribute");
+ diag->Error(DiagMessage(out_resource->source)
+ << "<" << tag_name << "> must have a 'type' attribute");
return false;
}
const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
if (!parsed_type) {
- diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
- << maybe_type.value()
- << "' in <public-group>");
+ diag->Error(DiagMessage(out_resource->source)
+ << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">");
return false;
}
- Maybe<StringPiece> maybe_id_str =
- xml::FindNonEmptyAttribute(parser, "first-id");
+ Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
if (!maybe_id_str) {
- diag_->Error(DiagMessage(out_resource->source)
- << "<public-group> must have a 'first-id' attribute");
+ diag->Error(DiagMessage(out_resource->source)
+ << "<" << tag_name << "> must have a 'first-id' attribute");
return false;
}
- Maybe<ResourceId> maybe_id =
- ResourceUtils::ParseResourceId(maybe_id_str.value());
+ Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
if (!maybe_id) {
- diag_->Error(DiagMessage(out_resource->source) << "invalid resource ID '"
- << maybe_id_str.value()
- << "' in <public-group>");
+ diag->Error(DiagMessage(out_resource->source)
+ << "invalid resource ID '" << maybe_id_str.value() << "' in <" << tag_name << ">");
return false;
}
- ResourceId next_id = maybe_id.value();
-
std::string comment;
+ ResourceId next_id = maybe_id.value();
bool error = false;
const size_t depth = parser->depth();
while (xml::XmlPullParser::NextChildNode(parser, depth)) {
@@ -949,53 +948,72 @@
continue;
}
- const Source item_source = source_.WithLine(parser->line_number());
+ const Source item_source = out_resource->source.WithLine(parser->line_number());
const std::string& element_namespace = parser->element_namespace();
const std::string& element_name = parser->element_name();
if (element_namespace.empty() && element_name == "public") {
- Maybe<StringPiece> maybe_name =
- xml::FindNonEmptyAttribute(parser, "name");
+ auto maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (!maybe_name) {
- diag_->Error(DiagMessage(item_source)
- << "<public> must have a 'name' attribute");
+ diag->Error(DiagMessage(item_source) << "<public> must have a 'name' attribute");
error = true;
continue;
}
if (xml::FindNonEmptyAttribute(parser, "id")) {
- diag_->Error(DiagMessage(item_source)
- << "'id' is ignored within <public-group>");
+ diag->Error(DiagMessage(item_source) << "'id' is ignored within <" << tag_name << ">");
error = true;
continue;
}
if (xml::FindNonEmptyAttribute(parser, "type")) {
- diag_->Error(DiagMessage(item_source)
- << "'type' is ignored within <public-group>");
+ diag->Error(DiagMessage(item_source) << "'type' is ignored within <" << tag_name << ">");
error = true;
continue;
}
- ParsedResource child_resource;
- child_resource.name.type = *parsed_type;
- child_resource.name.entry = maybe_name.value().to_string();
- child_resource.id = next_id;
- // NOLINTNEXTLINE(bugprone-use-after-move) move+reset comment
- child_resource.comment = std::move(comment);
- child_resource.source = item_source;
- child_resource.visibility_level = Visibility::Level::kPublic;
- out_resource->child_resources.push_back(std::move(child_resource));
+ ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{
+ .name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()},
+ .source = item_source,
+ .id = next_id,
+ .comment = std::move(comment),
+ });
- next_id.id += 1;
+ // Execute group specific code.
+ func(entry_res, next_id);
+ next_id.id++;
} else if (!ShouldIgnoreElement(element_namespace, element_name)) {
- diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
+ diag->Error(DiagMessage(item_source) << ":" << element_name << ">");
error = true;
}
}
return !error;
}
+bool ResourceParser::ParseStagingPublicGroup(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ return ParseGroupImpl(parser, out_resource, kStagingPublicGroupTag, diag_,
+ [](ParsedResource& parsed_entry, ResourceId id) {
+ parsed_entry.id = id;
+ parsed_entry.staged_api = true;
+ parsed_entry.visibility_level = Visibility::Level::kPublic;
+ });
+}
+
+bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+ if (options_.visibility) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << kPublicGroupTag << "> tag not allowed with --visibility flag");
+ return false;
+ }
+
+ return ParseGroupImpl(parser, out_resource, kPublicGroupTag, diag_,
+ [](ParsedResource& parsed_entry, ResourceId id) {
+ parsed_entry.id = id;
+ parsed_entry.visibility_level = Visibility::Level::kPublic;
+ });
+}
+
bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 9d3ecc8..af0db8c 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -99,6 +99,7 @@
bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 53bfa0b..4a509be 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -840,6 +840,31 @@
EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01010041)));
}
+TEST_F(ResourceParserTest, StagingPublicGroup) {
+ std::string input = R"(
+ <staging-public-group type="attr" first-id="0x01ff0049">
+ <public name="foo" />
+ <public name="bar" />
+ </staging-public-group>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+ ASSERT_TRUE(result);
+
+ ASSERT_TRUE(result.value().entry->id);
+ EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01ff0049)));
+ EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_TRUE(result.value().entry->visibility.staged_api);
+
+ result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
+ ASSERT_TRUE(result);
+
+ ASSERT_TRUE(result.value().entry->id);
+ EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01ff004a)));
+ EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_TRUE(result.value().entry->visibility.staged_api);
+}
+
TEST_F(ResourceParserTest, StrongestSymbolVisibilityWins) {
std::string input = R"(
<!-- private -->
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index cff9872..27f7bdd 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -47,11 +47,6 @@
}
template <typename T>
-bool less_than_type_and_id(const T& lhs, const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
- return lhs.id != rhs.second ? lhs.id < rhs.second : lhs.type < rhs.first;
-}
-
-template <typename T>
bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
}
@@ -80,12 +75,6 @@
return lhs.name.compare(0, lhs.name.size(), rhs.first.data(), rhs.first.size()) < 0;
}
-template <typename T, typename U>
-bool less_than_struct_with_name_and_id_pointer(const T* lhs,
- const std::pair<std::string_view, Maybe<U>>& rhs) {
- return less_than_struct_with_name_and_id(*lhs, rhs);
-}
-
template <typename T, typename Func, typename Elements>
T* FindElementsRunAction(const android::StringPiece& name, Elements& entries, Func action) {
const auto iter =
@@ -307,51 +296,115 @@
return CollisionResult::kConflict;
}
+template <typename T, typename Comparer>
+struct SortedVectorInserter : public Comparer {
+ std::pair<bool, typename std::vector<T>::iterator> LowerBound(std::vector<T>& el,
+ const T& value) {
+ auto it = std::lower_bound(el.begin(), el.end(), value, [&](auto& lhs, auto& rhs) {
+ return Comparer::operator()(lhs, rhs);
+ });
+ bool found =
+ it != el.end() && !Comparer::operator()(*it, value) && !Comparer::operator()(value, *it);
+ return std::make_pair(found, it);
+ }
+
+ T* Insert(std::vector<T>& el, T&& value) {
+ auto [found, it] = LowerBound(el, value);
+ if (found) {
+ return &*it;
+ }
+ return &*el.insert(it, std::move(value));
+ }
+};
+
+struct PackageViewComparer {
+ bool operator()(const ResourceTablePackageView& lhs, const ResourceTablePackageView& rhs) {
+ return less_than_struct_with_name_and_id<ResourceTablePackageView, uint8_t>(
+ lhs, std::make_pair(rhs.name, rhs.id));
+ }
+};
+
+struct TypeViewComparer {
+ bool operator()(const ResourceTableTypeView& lhs, const ResourceTableTypeView& rhs) {
+ return lhs.id != rhs.id ? lhs.id < rhs.id : lhs.type < rhs.type;
+ }
+};
+
+struct EntryViewComparer {
+ bool operator()(const ResourceEntry* lhs, const ResourceEntry* rhs) {
+ return less_than_struct_with_name_and_id<ResourceEntry, ResourceId>(
+ *lhs, std::make_pair(rhs->name, rhs->id));
+ }
+};
+
ResourceTableView ResourceTable::GetPartitionedView() const {
ResourceTableView view;
+ SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
+ SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
+ SortedVectorInserter<const ResourceEntry*, EntryViewComparer> entry_inserter;
+
for (const auto& package : packages) {
for (const auto& type : package->types) {
for (const auto& entry : type->entries) {
- std::pair<std::string_view, Maybe<uint8_t>> package_key(package->name, {});
- std::pair<std::string_view, Maybe<ResourceId>> entry_key(entry->name, {});
- std::pair<ResourceType, Maybe<uint8_t>> type_key(type->type, {});
- if (entry->id) {
- // If the entry has a defined id, use the id to determine insertion position.
- package_key.second = entry->id.value().package_id();
- type_key.second = entry->id.value().type_id();
- entry_key.second = entry->id.value();
- }
+ ResourceTablePackageView new_package{
+ package->name, entry->id ? entry->id.value().package_id() : Maybe<uint8_t>{}};
+ auto view_package = package_inserter.Insert(view.packages, std::move(new_package));
- auto package_it =
- std::lower_bound(view.packages.begin(), view.packages.end(), package_key,
- less_than_struct_with_name_and_id<ResourceTablePackageView, uint8_t>);
- if (package_it == view.packages.end() || package_it->name != package_key.first ||
- package_it->id != package_key.second) {
- ResourceTablePackageView new_package{std::string(package_key.first), package_key.second};
- package_it = view.packages.insert(package_it, new_package);
- }
-
- auto type_it = std::lower_bound(package_it->types.begin(), package_it->types.end(),
- type_key, less_than_type_and_id<ResourceTableTypeView>);
- if (type_it == package_it->types.end() || type_key.first != type_it->type ||
- type_it->id != type_key.second) {
- ResourceTableTypeView new_type{type_key.first, type_key.second};
- type_it = package_it->types.insert(type_it, new_type);
- }
+ ResourceTableTypeView new_type{type->type,
+ entry->id ? entry->id.value().type_id() : Maybe<uint8_t>{}};
+ auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
if (entry->visibility.level == Visibility::Level::kPublic) {
// Only mark the type visibility level as public, it doesn't care about being private.
- type_it->visibility_level = Visibility::Level::kPublic;
+ view_type->visibility_level = Visibility::Level::kPublic;
}
- auto entry_it =
- std::lower_bound(type_it->entries.begin(), type_it->entries.end(), entry_key,
- less_than_struct_with_name_and_id_pointer<ResourceEntry, ResourceId>);
- type_it->entries.insert(entry_it, entry.get());
+ entry_inserter.Insert(view_type->entries, entry.get());
}
}
}
+ // The android runtime does not support querying resources when the there are multiple type ids
+ // for the same resource type within the same package. For this reason, if there are types with
+ // multiple type ids, each type needs to exist in its own package in order to be queried by name.
+ std::vector<ResourceTablePackageView> new_packages;
+ for (auto& package : view.packages) {
+ // If a new package was already created for a different type within this package, then
+ // we can reuse those packages for other types that need to be extracted from this package.
+ // `start_index` is the index of the first newly created package that can be reused.
+ const size_t start_index = new_packages.size();
+ std::map<ResourceType, size_t> type_new_package_index;
+ for (auto type_it = package.types.begin(); type_it != package.types.end();) {
+ auto& type = *type_it;
+ auto type_index_iter = type_new_package_index.find(type.type);
+ if (type_index_iter == type_new_package_index.end()) {
+ // First occurrence of the resource type in this package. Keep it in this package.
+ type_new_package_index.insert(type_index_iter, std::make_pair(type.type, start_index));
+ ++type_it;
+ continue;
+ }
+
+ // The resource type has already been seen for this package, so this type must be extracted to
+ // a new separate package.
+ const size_t index = type_index_iter->second;
+ if (new_packages.size() == index) {
+ new_packages.emplace_back(ResourceTablePackageView{package.name, package.id});
+ type_new_package_index[type.type] = index + 1;
+ }
+
+ // Move the type into a new package
+ auto& other_package = new_packages[index];
+ type_inserter.Insert(other_package.types, std::move(type));
+ type_it = package.types.erase(type_it);
+ }
+ }
+
+ for (auto& new_package : new_packages) {
+ // Insert newly created packages after their original packages
+ auto [_, it] = package_inserter.LowerBound(view.packages, new_package);
+ view.packages.insert(++it, std::move(new_package));
+ }
+
return view;
}
@@ -424,6 +477,10 @@
// This symbol definition takes precedence, replace.
entry->visibility = res.visibility.value();
}
+
+ if (res.visibility->staged_api) {
+ entry->visibility.staged_api = entry->visibility.staged_api;
+ }
}
if (res.overlayable.has_value()) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 49392a5..080ecc2 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -51,6 +51,11 @@
Level level = Level::kUndefined;
Source source;
std::string comment;
+
+ // Indicates that the resource id may change across builds and that the public R.java identifier
+ // for this resource should not be final. This is set to `true` for resources in `staging-group`
+ // tags.
+ bool staged_api = false;
};
// Represents <add-resource> in an overlay.
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index b1e1a77..4247ec5 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -132,6 +132,11 @@
// The comment associated with the <public> tag.
string comment = 3;
+
+ // Indicates that the resource id may change across builds and that the public R.java identifier
+ // for this resource should not be final. This is set to `true` for resources in `staging-group`
+ // tags.
+ bool staged_api = 4;
}
// Whether a resource comes from a compile-time overlay and is explicitly allowed to not overlay an
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 27cbe88..dfdac6b 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -25,6 +25,7 @@
using testing::Eq;
using testing::HasSubstr;
using testing::Ne;
+using testing::NotNull;
namespace aapt {
@@ -400,4 +401,127 @@
EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
}
+TEST_F(LinkTest, StagedAndroidApi) {
+ StdErrDiagnostics diag;
+ const std::string android_values =
+ R"(<resources>
+ <public type="attr" name="finalized_res" id="0x01010001"/>
+
+ <!-- S staged attributes (support staged resources in the same type id) -->
+ <staging-public-group type="attr" first-id="0x01010050">
+ <public name="staged_s_res" />
+ </staging-public-group>
+
+ <!-- SV2 staged attributes (support staged resources in a separate type id) -->
+ <staging-public-group type="attr" first-id="0x01ff0049">
+ <public name="staged_s2_res" />
+ </staging-public-group>
+
+ <!-- T staged attributes (support staged resources in multiple separate type ids) -->
+ <staging-public-group type="attr" first-id="0x01fe0063">
+ <public name="staged_t_res" />
+ </staging-public-group>
+
+ <staging-public-group type="string" first-id="0x01fd0072">
+ <public name="staged_t_string" />
+ </staging-public-group>
+
+ <attr name="finalized_res" />
+ <attr name="staged_s_res" />
+ <attr name="staged_s2_res" />
+ <attr name="staged_t_res" />
+ <string name="staged_t_string">Hello</string>
+ </resources>)";
+
+ const std::string app_values =
+ R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <attr name="bar" />
+ <declare-styleable name="ClientStyleable">
+ <attr name="android:finalized_res" />
+ <attr name="android:staged_s_res" />
+ <attr name="bar" />
+ </declare-styleable>
+ </resources>)";
+
+ const std::string android_res = GetTestPath("android-res");
+ ASSERT_TRUE(
+ CompileFile(GetTestPath("res/values/values.xml"), android_values, android_res, &diag));
+
+ const std::string android_apk = GetTestPath("android.apk");
+ const std::string android_java = GetTestPath("android_java");
+ // clang-format off
+ auto android_manifest = ManifestBuilder(this)
+ .SetPackageName("android")
+ .Build();
+
+ auto android_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(android_manifest)
+ .AddParameter("--private-symbols", "com.android.internal")
+ .AddParameter("--java", android_java)
+ .AddCompiledResDir(android_res, &diag)
+ .Build(android_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(android_link_args, &diag));
+
+ const std::string android_r_java = android_java + "/android/R.java";
+ std::string android_r_contents;
+ ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static final int finalized_res=0x01010001;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_s_res=0x01010050;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_s2_res=0x01ff0049;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_t_res=0x01fe0063;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_t_string=0x01fd0072;"));
+
+ // Build an app that uses the framework attribute in a declare-styleable
+ const std::string client_res = GetTestPath("app-res");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), app_values, client_res, &diag));
+
+ const std::string app_apk = GetTestPath("app.apk");
+ const std::string app_java = GetTestPath("app_java");
+ // clang-format off
+ auto app_manifest = ManifestBuilder(this)
+ .SetPackageName("com.example.app")
+ .Build();
+
+ auto app_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(app_manifest)
+ .AddParameter("--java", app_java)
+ .AddParameter("-I", android_apk)
+ .AddCompiledResDir(client_res, &diag)
+ .Build(app_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(app_link_args, &diag));
+
+ const std::string client_r_java = app_java + "/com/example/app/R.java";
+ std::string client_r_contents;
+ ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
+ EXPECT_THAT(client_r_contents, HasSubstr(" 0x01010001, android.R.attr.staged_s_res, 0x7f010000"));
+
+ // Test that the resource ids of staged and non-staged resource can be retrieved
+ android::AssetManager2 am;
+ auto android_asset = android::ApkAssets::Load(android_apk);
+ ASSERT_THAT(android_asset, NotNull());
+ ASSERT_TRUE(am.SetApkAssets({android_asset.get()}));
+
+ auto result = am.GetResourceId("android:attr/finalized_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01010001));
+
+ result = am.GetResourceId("android:attr/staged_s_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01010050));
+
+ result = am.GetResourceId("android:attr/staged_s2_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01ff0049));
+
+ result = am.GetResourceId("android:attr/staged_t_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01fe0063));
+
+ result = am.GetResourceId("android:string/staged_t_string");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01fd0072));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 07db73d..9a50b26 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -77,6 +77,27 @@
NextIdFinder<uint16_t, ResourceName> next_entry_id_;
};
+struct ResourceTypeKey {
+ ResourceType type;
+ uint8_t id;
+
+ bool operator<(const ResourceTypeKey& other) const {
+ return (type != other.type) ? type < other.type : id < other.id;
+ }
+
+ bool operator==(const ResourceTypeKey& other) const {
+ return type == other.type && id == other.id;
+ }
+
+ bool operator!=(const ResourceTypeKey& other) const {
+ return !(*this == other);
+ }
+};
+
+::std::ostream& operator<<(::std::ostream& out, const ResourceTypeKey& type) {
+ return out << type.type;
+}
+
struct IdAssignerContext {
IdAssignerContext(std::string package_name, uint8_t package_id)
: package_name_(std::move(package_name)), package_id_(package_id) {
@@ -85,7 +106,8 @@
// Attempts to reserve the resource id for the specified resource name.
// Returns whether the id was reserved successfully.
// Reserving identifiers must be completed before `NextId` is called for the first time.
- bool ReserveId(const ResourceName& name, ResourceId id, IDiagnostics* diag);
+ bool ReserveId(const ResourceName& name, ResourceId id, const Visibility& visibility,
+ IDiagnostics* diag);
// Retrieves the next available resource id that has not been reserved.
std::optional<ResourceId> NextId(const ResourceName& name, IDiagnostics* diag);
@@ -93,8 +115,10 @@
private:
std::string package_name_;
uint8_t package_id_;
- std::map<ResourceType, TypeGroup> types_;
- NextIdFinder<uint8_t, ResourceType> type_id_finder_ = NextIdFinder<uint8_t, ResourceType>(1);
+ std::map<ResourceTypeKey, TypeGroup> types_;
+ std::map<ResourceType, uint8_t> non_staged_type_ids_;
+ NextIdFinder<uint8_t, ResourceTypeKey> type_id_finder_ =
+ NextIdFinder<uint8_t, ResourceTypeKey>(1);
};
} // namespace
@@ -106,7 +130,8 @@
for (auto& entry : type->entries) {
const ResourceName name(package->name, type->type, entry->name);
if (entry->id) {
- if (!assigned_ids.ReserveId(name, entry->id.value(), context->GetDiagnostics())) {
+ if (!assigned_ids.ReserveId(name, entry->id.value(), entry->visibility,
+ context->GetDiagnostics())) {
return false;
}
}
@@ -116,7 +141,8 @@
const auto iter = assigned_id_map_->find(name);
if (iter != assigned_id_map_->end()) {
const ResourceId assigned_id = iter->second;
- if (!assigned_ids.ReserveId(name, assigned_id, context->GetDiagnostics())) {
+ if (!assigned_ids.ReserveId(name, assigned_id, entry->visibility,
+ context->GetDiagnostics())) {
return false;
}
entry->id = assigned_id;
@@ -132,7 +158,8 @@
for (const auto& stable_id_entry : *assigned_id_map_) {
const ResourceName& pre_assigned_name = stable_id_entry.first;
const ResourceId& pre_assigned_id = stable_id_entry.second;
- if (!assigned_ids.ReserveId(pre_assigned_name, pre_assigned_id, context->GetDiagnostics())) {
+ if (!assigned_ids.ReserveId(pre_assigned_name, pre_assigned_id, {},
+ context->GetDiagnostics())) {
return false;
}
}
@@ -165,7 +192,7 @@
auto assign_result = pre_assigned_ids_.emplace(id, key);
if (!assign_result.second && assign_result.first->second != key) {
std::stringstream error;
- error << "ID " << id << " is already assigned to " << assign_result.first->second;
+ error << "ID is already assigned to " << assign_result.first->second;
return unexpected(error.str());
}
return id;
@@ -210,7 +237,7 @@
if (type_id_ != id.type_id()) {
// Currently there cannot be multiple type ids for a single type.
std::stringstream error;
- error << "type '" << name.type << "' already has ID " << id.type_id();
+ error << "type '" << name.type << "' already has ID " << std::hex << (int)id.type_id();
return unexpected(error.str());
}
@@ -234,24 +261,38 @@
return ResourceId(package_id_, type_id_, entry_id.value());
}
-bool IdAssignerContext::ReserveId(const ResourceName& name, ResourceId id, IDiagnostics* diag) {
+bool IdAssignerContext::ReserveId(const ResourceName& name, ResourceId id,
+ const Visibility& visibility, IDiagnostics* diag) {
if (package_id_ != id.package_id()) {
diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
- << " because package already has ID " << id.package_id());
+ << " because package already has ID " << std::hex
+ << (int)id.package_id());
return false;
}
- auto type = types_.find(name.type);
+ auto key = ResourceTypeKey{name.type, id.type_id()};
+ auto type = types_.find(key);
if (type == types_.end()) {
// The type has not been assigned an id yet. Ensure that the specified id is not being used by
// another type.
- auto assign_result = type_id_finder_.ReserveId(name.type, id.type_id());
+ auto assign_result = type_id_finder_.ReserveId(key, id.type_id());
if (!assign_result.has_value()) {
diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
<< " because type " << assign_result.error());
return false;
}
- type = types_.emplace(name.type, TypeGroup(package_id_, id.type_id())).first;
+ type = types_.emplace(key, TypeGroup(package_id_, id.type_id())).first;
+ }
+
+ if (!visibility.staged_api) {
+ // Ensure that non-staged resources can only exist in one type ID.
+ auto non_staged_type = non_staged_type_ids_.emplace(name.type, id.type_id());
+ if (!non_staged_type.second && non_staged_type.first->second != id.type_id()) {
+ diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
+ << " because type already has ID " << std::hex
+ << (int)id.type_id());
+ return false;
+ }
}
auto assign_result = type->second.ReserveId(name, id);
@@ -268,11 +309,19 @@
// The package name is not known during the compile stage.
// Resources without a package name are considered a part of the app being linked.
CHECK(name.package.empty() || name.package == package_name_);
- auto type = types_.find(name.type);
- if (type == types_.end()) {
+
+ // Find the type id for non-staged resources of this type.
+ auto non_staged_type = non_staged_type_ids_.find(name.type);
+ if (non_staged_type == non_staged_type_ids_.end()) {
auto next_type_id = type_id_finder_.NextId();
CHECK(next_type_id.has_value()) << "resource type IDs allocated have exceeded maximum (256)";
- type = types_.emplace(name.type, TypeGroup(package_id_, next_type_id.value())).first;
+ non_staged_type = non_staged_type_ids_.emplace(name.type, *next_type_id).first;
+ }
+
+ ResourceTypeKey key{name.type, non_staged_type->second};
+ auto type = types_.find(key);
+ if (type == types_.end()) {
+ type = types_.emplace(key, TypeGroup(package_id_, key.id)).first;
}
auto assign_result = type->second.NextId();
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index 0065a22..6637766 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -98,6 +98,37 @@
ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
}
+TEST_F(IdAssignerTests, FailWhenNonUniqueTypeIdsAssigned) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:string/foo", ResourceId(0x01040000))
+ .AddSimple("android:attr/bar", ResourceId(0x01040006))
+ .Build();
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
+TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIds) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo", ResourceId(0x01050000))
+ .AddSimple("android:attr/bar", ResourceId(0x01040006))
+ .Build();
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
+TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIdsRegardlessOfStagedId) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo", ResourceId(0x01050000))
+ .AddSimple("android:attr/bar", ResourceId(0x01ff0006))
+ .Add(NewResourceBuilder("android:attr/staged_baz")
+ .SetId(0x01ff0000)
+ .SetVisibility({.staged_api = true})
+ .Build())
+ .Build();
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
TEST_F(IdAssignerTests, AssignIdsWithIdMap) {
auto table = test::ResourceTableBuilder()
.AddSimple("android:attr/foo")
@@ -154,52 +185,24 @@
}
::testing::AssertionResult VerifyIds(ResourceTable* table) {
- std::set<uint8_t> package_ids;
- auto table_view = table->GetPartitionedView();
- for (auto& package : table_view.packages) {
- if (!package.id) {
- return ::testing::AssertionFailure() << "package " << package.name << " has no ID";
- }
-
- if (!package_ids.insert(package.id.value()).second) {
- return ::testing::AssertionFailure() << "package " << package.name << " has non-unique ID "
- << std::hex << (int)package.id.value() << std::dec;
- }
- }
-
- for (auto& package : table_view.packages) {
- std::set<uint8_t> type_ids;
- for (auto& type : package.types) {
- if (!type.id) {
- return ::testing::AssertionFailure()
- << "type " << type.type << " of package " << package.name << " has no ID";
- }
-
- if (!type_ids.insert(type.id.value()).second) {
- return ::testing::AssertionFailure()
- << "type " << type.type << " of package " << package.name << " has non-unique ID "
- << std::hex << (int)type.id.value() << std::dec;
- }
- }
-
- for (auto& type : package.types) {
- std::set<ResourceId> entry_ids;
- for (auto& entry : type.entries) {
+ std::set<ResourceId> seen_ids;
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
if (!entry->id) {
return ::testing::AssertionFailure()
- << "entry " << entry->name << " of type " << type.type << " of package "
- << package.name << " has no ID";
+ << "resource " << ResourceNameRef(package->name, type->type, entry->name)
+ << " has no ID";
}
-
- if (!entry_ids.insert(entry->id.value()).second) {
+ if (!seen_ids.insert(entry->id.value()).second) {
return ::testing::AssertionFailure()
- << "entry " << entry->name << " of type " << type.type << " of package "
- << package.name << " has non-unique ID " << std::hex << entry->id.value()
- << std::dec;
+ << "resource " << ResourceNameRef(package->name, type->type, entry->name)
+ << " has a non-unique ID" << std::hex << entry->id.value() << std::dec;
}
}
}
}
+
return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 5fea897..17d11a6 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -546,8 +546,12 @@
const uint16_t entry_id = entry->id.value().entry_id();
// Populate the config masks for this entry.
+ uint32_t& entry_config_masks = config_masks[entry_id];
if (entry->visibility.level == Visibility::Level::kPublic) {
- config_masks[entry_id] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ }
+ if (entry->visibility.staged_api) {
+ entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_STAGED_API);
}
const size_t config_count = entry->values.size();
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index bfb92da..498d5a2 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -456,6 +456,7 @@
DeserializeSourceFromPb(pb_visibility.source(), src_pool, &entry->visibility.source);
}
entry->visibility.comment = pb_visibility.comment();
+ entry->visibility.staged_api = pb_visibility.staged_api();
const Visibility::Level level = DeserializeVisibilityFromPb(pb_visibility.level());
entry->visibility.level = level;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 9842e25..f13f82d 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -378,6 +378,7 @@
// Write the Visibility struct.
pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
+ pb_visibility->set_staged_api(entry->visibility.staged_api);
pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
if (source_pool != nullptr) {
SerializeSourceToPb(entry->visibility.source, source_pool.get(),
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index ad5ed4d..591ba149 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -50,6 +50,53 @@
return (result) ? result.value().entry : nullptr;
}
+TEST(ProtoSerializeTest, SerializeVisibility) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .Add(NewResourceBuilder("com.app.a:bool/foo")
+ .SetVisibility({Visibility::Level::kUndefined})
+ .Build())
+ .Add(NewResourceBuilder("com.app.a:bool/bar")
+ .SetVisibility({Visibility::Level::kPrivate})
+ .Build())
+ .Add(NewResourceBuilder("com.app.a:bool/baz")
+ .SetVisibility({Visibility::Level::kPublic})
+ .Build())
+ .Add(NewResourceBuilder("com.app.a:bool/fiz")
+ .SetVisibility({.level = Visibility::Level::kPublic, .staged_api = true})
+ .Build())
+ .Build();
+
+ ResourceTable new_table;
+ pb::ResourceTable pb_table;
+ MockFileCollection files;
+ std::string error;
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ auto search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+ EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPrivate));
+ EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_TRUE(search_result.value().entry->visibility.staged_api);
+}
+
TEST(ProtoSerializeTest, SerializeSinglePackage) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 995495a..d3648c8 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -59,8 +59,9 @@
template <typename T>
class PrimitiveMember : public ClassMember {
public:
- PrimitiveMember(const android::StringPiece& name, const T& val)
- : name_(name.to_string()), val_(val) {}
+ PrimitiveMember(const android::StringPiece& name, const T& val, bool staged_api = false)
+ : name_(name.to_string()), val_(val), staged_api_(staged_api) {
+ }
bool empty() const override {
return false;
@@ -77,7 +78,7 @@
ClassMember::Print(final, printer, strip_api_annotations);
printer->Print("public static ");
- if (final) {
+ if (final && !staged_api_) {
printer->Print("final ");
}
printer->Print("int ").Print(name_).Print("=").Print(to_string(val_)).Print(";");
@@ -88,14 +89,16 @@
std::string name_;
T val_;
+ bool staged_api_;
};
// Specialization for strings so they get the right type and are quoted with "".
template <>
class PrimitiveMember<std::string> : public ClassMember {
public:
- PrimitiveMember(const android::StringPiece& name, const std::string& val)
- : name_(name.to_string()), val_(val) {}
+ PrimitiveMember(const android::StringPiece& name, const std::string& val, bool staged_api = false)
+ : name_(name.to_string()), val_(val) {
+ }
bool empty() const override {
return false;
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 039448e..e1e2e01 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -460,7 +460,8 @@
const std::string field_name = TransformToFieldName(name.entry);
if (out_class_def != nullptr) {
- auto resource_member = util::make_unique<ResourceMember>(field_name, real_id);
+ auto resource_member =
+ util::make_unique<ResourceMember>(field_name, real_id, entry.visibility.staged_api);
// Build the comments and annotations for this entry.
AnnotationProcessor* processor = resource_member->GetCommentBuilder();
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index de72334..d385267 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -199,7 +199,8 @@
if (sr.entry->id) {
symbol->id = sr.entry->id.value();
- symbol->is_dynamic = (sr.entry->id.value().package_id() == 0);
+ symbol->is_dynamic =
+ (sr.entry->id.value().package_id() == 0) || sr.entry->visibility.staged_api;
}
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
@@ -374,7 +375,8 @@
if (s) {
s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package);
+ s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package) ||
+ (type_spec_flags & android::ResTable_typeSpec::SPEC_STAGED_API) != 0;
return s;
}
return {};
@@ -421,7 +423,8 @@
if (s) {
s->is_public = (*flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package);
+ s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package) ||
+ (*flags & android::ResTable_typeSpec::SPEC_STAGED_API) != 0;
return s;
}
return {};