diff options
238 files changed, 6109 insertions, 1099 deletions
diff --git a/INPUT_OWNERS b/INPUT_OWNERS index 44b2f3805495..06ead06fc13a 100644 --- a/INPUT_OWNERS +++ b/INPUT_OWNERS @@ -1,4 +1,6 @@ # Bug component: 136048 +arpitks@google.com +asmitapoddar@google.com hcutts@google.com joseprio@google.com michaelwr@google.com @@ -32,6 +32,7 @@ per-file **.bp,**.mk = hansson@google.com, joeo@google.com, lamontjones@google.c per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS +per-file INPUT_OWNERS = file:/INPUT_OWNERS per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS per-file SQLITE_OWNERS = file:/SQLITE_OWNERS diff --git a/core/api/system-current.txt b/core/api/system-current.txt index d9b5d56544d6..dfd915b75572 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -327,7 +327,7 @@ package android { field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE"; field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER"; field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER"; - field @FlaggedApi("com.android.net.flags.register_nsd_offload_engine") public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE"; + field @FlaggedApi("android.net.platform.flags.register_nsd_offload_engine") public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE"; field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION"; field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM"; field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER"; diff --git a/core/java/Android.bp b/core/java/Android.bp index 184421518d32..ab1c9a4ef48d 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -528,6 +528,13 @@ filegroup { ], } +java_library { + name: "protolog-group", + srcs: [ + "com/android/internal/protolog/common/IProtoLogGroup.java", + ], +} + // PackageManager common filegroup { name: "framework-pm-common-shared-srcs", diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 22b8d92b507c..074f7e993eb4 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5998,9 +5998,11 @@ public final class ActivityThread extends ClientTransactionHandler } @Override - public ActivityClientRecord prepareRelaunchActivity(IBinder token, - List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, - int configChanges, MergedConfiguration config, boolean preserveWindow) { + public ActivityClientRecord prepareRelaunchActivity(@NonNull IBinder token, + @Nullable List<ResultInfo> pendingResults, + @Nullable List<ReferrerIntent> pendingNewIntents, int configChanges, + @NonNull MergedConfiguration config, boolean preserveWindow, + @NonNull ActivityWindowInfo activityWindowInfo) { ActivityClientRecord target = null; boolean scheduleRelaunch = false; @@ -6041,14 +6043,15 @@ public final class ActivityThread extends ClientTransactionHandler target.createdConfig = config.getGlobalConfiguration(); target.overrideConfig = config.getOverrideConfiguration(); target.pendingConfigChanges |= configChanges; + target.mActivityWindowInfo = activityWindowInfo; } return scheduleRelaunch ? target : null; } @Override - public void handleRelaunchActivity(ActivityClientRecord tmp, - PendingTransactionActions pendingActions) { + public void handleRelaunchActivity(@NonNull ActivityClientRecord tmp, + @NonNull PendingTransactionActions pendingActions) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); @@ -6128,7 +6131,8 @@ public final class ActivityThread extends ClientTransactionHandler r.activity.mChangingConfigurations = true; handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents, - pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity"); + pendingActions, tmp.startsNotResumed, tmp.overrideConfig, tmp.mActivityWindowInfo, + "handleRelaunchActivity"); } void scheduleRelaunchActivity(IBinder token) { @@ -6183,7 +6187,8 @@ public final class ActivityThread extends ClientTransactionHandler r.overrideConfig); final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain( r.token, null /* pendingResults */, null /* pendingIntents */, - 0 /* configChanges */, mergedConfiguration, r.mPreserveWindow); + 0 /* configChanges */, mergedConfiguration, r.mPreserveWindow, + r.getActivityWindowInfo()); // Make sure to match the existing lifecycle state in the end of the transaction. final ActivityLifecycleItem lifecycleRequest = TransactionExecutorHelper.getLifecycleRequestForCurrentState(r); @@ -6194,10 +6199,12 @@ public final class ActivityThread extends ClientTransactionHandler executeTransaction(transaction); } - private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges, - List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents, - PendingTransactionActions pendingActions, boolean startsNotResumed, - Configuration overrideConfig, String reason) { + private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r, int configChanges, + @Nullable List<ResultInfo> pendingResults, + @Nullable List<ReferrerIntent> pendingIntents, + @NonNull PendingTransactionActions pendingActions, boolean startsNotResumed, + @NonNull Configuration overrideConfig, @NonNull ActivityWindowInfo activityWindowInfo, + @NonNull String reason) { // Preserve last used intent, it may be set from Activity#setIntent(). final Intent customIntent = r.activity.mIntent; // Need to ensure state is saved. @@ -6230,6 +6237,7 @@ public final class ActivityThread extends ClientTransactionHandler } r.startsNotResumed = startsNotResumed; r.overrideConfig = overrideConfig; + r.mActivityWindowInfo = activityWindowInfo; handleLaunchActivity(r, pendingActions, mLastReportedDeviceId, customIntent); } diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index f6373d690793..6ad03135ea02 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -377,6 +377,9 @@ public final class AutomaticZenRule implements Parcelable { * Sets the {@link ZenDeviceEffects} associated to this rule. Device effects specify changes to * the device behavior that should apply while the rule is active, but are not directly related * to suppressing notifications (for example: disabling always-on display). + * + * <p>When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule}, + * a {@code null} value here means the previous set of effects is retained. */ @FlaggedApi(Flags.FLAG_MODES_API) public void setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) { @@ -749,6 +752,9 @@ public final class AutomaticZenRule implements Parcelable { /** * Sets the zen policy. + * + * <p>When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule}, + * a {@code null} value here means the previous policy is retained. */ public @NonNull Builder setZenPolicy(@Nullable ZenPolicy policy) { mPolicy = policy; @@ -759,6 +765,9 @@ public final class AutomaticZenRule implements Parcelable { * Sets the {@link ZenDeviceEffects} associated to this rule. Device effects specify changes * to the device behavior that should apply while the rule is active, but are not directly * related to suppressing notifications (for example: disabling always-on display). + * + * <p>When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule}, + * a {@code null} value here means the previous set of effects is retained. */ @NonNull public Builder setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) { diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index b30067491a84..4c92dee6ff17 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -30,6 +30,7 @@ import android.content.res.Configuration; import android.os.IBinder; import android.util.MergedConfiguration; import android.view.SurfaceControl; +import android.window.ActivityWindowInfo; import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.WindowContext; import android.window.WindowContextInfo; @@ -232,12 +233,15 @@ public abstract class ClientTransactionHandler { * @param config New configuration applied to the activity. * @param preserveWindow Whether the activity should try to reuse the window it created, * including the decor view after the relaunch. + * @param activityWindowInfo Window information about the relaunched Activity. * @return An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during * relaunch, or {@code null} if relaunch cancelled. */ - public abstract ActivityClientRecord prepareRelaunchActivity(IBinder token, - List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, - int configChanges, MergedConfiguration config, boolean preserveWindow); + public abstract ActivityClientRecord prepareRelaunchActivity(@NonNull IBinder token, + @Nullable List<ResultInfo> pendingResults, + @Nullable List<ReferrerIntent> pendingNewIntents, int configChanges, + @NonNull MergedConfiguration config, boolean preserveWindow, + @NonNull ActivityWindowInfo activityWindowInfo); /** * Perform activity relaunch. @@ -245,7 +249,7 @@ public abstract class ClientTransactionHandler { * @param pendingActions Pending actions to be used on later stages of activity transaction. * */ public abstract void handleRelaunchActivity(@NonNull ActivityClientRecord r, - PendingTransactionActions pendingActions); + @NonNull PendingTransactionActions pendingActions); /** * Report that relaunch request was handled. diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 83fb393da125..02d694436bfe 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -981,12 +981,12 @@ public class WallpaperManager { /** * <strong> Important note: </strong> * <ul> - * <li>Up to version S, this method requires the + * <li>Up to Android 12, this method requires the * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li> - * <li>Starting in T, directly accessing the wallpaper is not possible anymore, + * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore, * instead the default system wallpaper is returned - * (some versions of T may throw a {@code SecurityException}).</li> - * <li>From version U, this method should not be used + * (some versions of Android 13 may throw a {@code SecurityException}).</li> + * <li>From Android 14, this method should not be used * and will always throw a {@code SecurityException}.</li> * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} * can still access the real wallpaper on all versions. </li> @@ -1265,12 +1265,12 @@ public class WallpaperManager { /** * <strong> Important note: </strong> * <ul> - * <li>Up to version S, this method requires the + * <li>Up to Android 12, this method requires the * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li> - * <li>Starting in T, directly accessing the wallpaper is not possible anymore, + * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore, * instead the default system wallpaper is returned - * (some versions of T may throw a {@code SecurityException}).</li> - * <li>From version U, this method should not be used + * (some versions of Android 13 may throw a {@code SecurityException}).</li> + * <li>From Android 14, this method should not be used * and will always throw a {@code SecurityException}.</li> * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} * can still access the real wallpaper on all versions. </li> @@ -1318,12 +1318,12 @@ public class WallpaperManager { /** * <strong> Important note: </strong> * <ul> - * <li>Up to version S, this method requires the + * <li>Up to Android 12, this method requires the * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li> - * <li>Starting in T, directly accessing the wallpaper is not possible anymore, + * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore, * instead the default wallpaper is returned - * (some versions of T may throw a {@code SecurityException}).</li> - * <li>From version U, this method should not be used + * (some versions of Android 13 may throw a {@code SecurityException}).</li> + * <li>From Android 14, this method should not be used * and will always throw a {@code SecurityException}.</li> * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} * can still access the real wallpaper on all versions. </li> @@ -1677,12 +1677,12 @@ public class WallpaperManager { /** * <strong> Important note: </strong> * <ul> - * <li>Up to version S, this method requires the + * <li>Up to Android 12, this method requires the * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li> - * <li>Starting in T, directly accessing the wallpaper is not possible anymore, + * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore, * instead the default system wallpaper is returned - * (some versions of T may throw a {@code SecurityException}).</li> - * <li>From version U, this method should not be used + * (some versions of Android 13 may throw a {@code SecurityException}).</li> + * <li>From Android 14, this method should not be used * and will always throw a {@code SecurityException}.</li> * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} * can still access the real wallpaper on all versions. </li> @@ -1904,14 +1904,14 @@ public class WallpaperManager { * caller doesn't have the appropriate permissions, this returns {@code null}. * * <p> - * Before Android U, this method requires the + * For devices running Android 13 or earlier, this method requires the * {@link android.Manifest.permission#QUERY_ALL_PACKAGES} permission. * </p> * * <p> - * Starting from Android U, in order to use this, apps should declare a {@code <queries>} tag - * with the action {@code "android.service.wallpaper.WallpaperService"}. Otherwise, - * this method will return {@code null} if the caller doesn't otherwise have + * For devices running Android 14 or later, in order to use this, apps should declare a + * {@code <queries>} tag with the action {@code "android.service.wallpaper.WallpaperService"}. + * Otherwise, this method will return {@code null} if the caller doesn't otherwise have * <a href="{@docRoot}training/package-visibility">visibility</a> of the wallpaper package. * </p> */ diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java index cbb0ae784f82..6da871a74383 100644 --- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java +++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java @@ -30,6 +30,7 @@ import android.os.Parcel; import android.os.Trace; import android.util.MergedConfiguration; import android.util.Slog; +import android.window.ActivityWindowInfo; import com.android.internal.content.ReferrerIntent; @@ -50,6 +51,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { private int mConfigChanges; private MergedConfiguration mConfig; private boolean mPreserveWindow; + private ActivityWindowInfo mActivityWindowInfo; /** * A record that was properly configured for relaunch. Execution will be cancelled if not @@ -64,7 +66,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { CompatibilityInfo.applyOverrideScaleIfNeeded(mConfig); } mActivityClientRecord = client.prepareRelaunchActivity(getActivityToken(), mPendingResults, - mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow); + mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow, mActivityWindowInfo); } @Override @@ -101,7 +103,8 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { public static ActivityRelaunchItem obtain(@NonNull IBinder activityToken, @Nullable List<ResultInfo> pendingResults, @Nullable List<ReferrerIntent> pendingNewIntents, int configChanges, - @NonNull MergedConfiguration config, boolean preserveWindow) { + @NonNull MergedConfiguration config, boolean preserveWindow, + @NonNull ActivityWindowInfo activityWindowInfo) { ActivityRelaunchItem instance = ObjectPool.obtain(ActivityRelaunchItem.class); if (instance == null) { instance = new ActivityRelaunchItem(); @@ -113,6 +116,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { instance.mConfigChanges = configChanges; instance.mConfig = new MergedConfiguration(config); instance.mPreserveWindow = preserveWindow; + instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo); return instance; } @@ -126,6 +130,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { mConfig = null; mPreserveWindow = false; mActivityClientRecord = null; + mActivityWindowInfo = null; ObjectPool.recycle(this); } @@ -141,6 +146,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { dest.writeInt(mConfigChanges); dest.writeTypedObject(mConfig, flags); dest.writeBoolean(mPreserveWindow); + dest.writeTypedObject(mActivityWindowInfo, flags); } /** Read from Parcel. */ @@ -151,6 +157,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { mConfigChanges = in.readInt(); mConfig = in.readTypedObject(MergedConfiguration.CREATOR); mPreserveWindow = in.readBoolean(); + mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR); } public static final @NonNull Creator<ActivityRelaunchItem> CREATOR = @@ -176,7 +183,8 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { return Objects.equals(mPendingResults, other.mPendingResults) && Objects.equals(mPendingNewIntents, other.mPendingNewIntents) && mConfigChanges == other.mConfigChanges && Objects.equals(mConfig, other.mConfig) - && mPreserveWindow == other.mPreserveWindow; + && mPreserveWindow == other.mPreserveWindow + && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo); } @Override @@ -188,6 +196,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { result = 31 * result + mConfigChanges; result = 31 * result + Objects.hashCode(mConfig); result = 31 * result + (mPreserveWindow ? 1 : 0); + result = 31 * result + Objects.hashCode(mActivityWindowInfo); return result; } @@ -198,6 +207,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { + ",pendingNewIntents=" + mPendingNewIntents + ",configChanges=" + mConfigChanges + ",config=" + mConfig - + ",preserveWindow" + mPreserveWindow + "}"; + + ",preserveWindow=" + mPreserveWindow + + ",activityWindowInfo=" + mActivityWindowInfo + "}"; } } diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 22e233aebf36..ac80561c3b50 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -177,3 +177,10 @@ flag { description: "Enable the sensitive notifications toggle to be visible in the Private space settings page" bug: "317067050" } + +flag { + name: "enable_private_space_intent_redirection" + namespace: "profile_experiences" + description: "Enable Private Space telephony and SMS intent redirection to the main user" + bug: "325576602" +} diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig index 6efb87225183..9f9aef8acc6a 100644 --- a/core/java/android/net/flags.aconfig +++ b/core/java/android/net/flags.aconfig @@ -17,3 +17,9 @@ flag { bug: "307898240" } +flag { + name: "register_nsd_offload_engine" + namespace: "android_core_networking" + description: "Flag for registerOffloadEngine API in NsdManager" + bug: "294777050" +} diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 13b9c4518711..f5f4fd9f7042 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -480,11 +480,19 @@ public final class PointerIcon implements Parcelable { mBitmapFrames = new Bitmap[frames - 1]; final int width = drawable.getIntrinsicWidth(); final int height = drawable.getIntrinsicHeight(); + final boolean isVectorAnimation = drawable instanceof VectorDrawable; + mDrawNativeDropShadow = isVectorAnimation; for (int i = 1; i < frames; ++i) { Drawable drawableFrame = animationDrawable.getFrame(i); - if (!(drawableFrame instanceof BitmapDrawable)) { + if (!(drawableFrame instanceof BitmapDrawable) + && !(drawableFrame instanceof VectorDrawable)) { throw new IllegalArgumentException("Frame of an animated pointer icon " - + "must refer to a bitmap drawable."); + + "must refer to a bitmap drawable or vector drawable."); + } + if (isVectorAnimation != (drawableFrame instanceof VectorDrawable)) { + throw new IllegalArgumentException("The drawable of the " + i + "-th frame " + + "is a different type from the others. All frames should be the " + + "same type."); } if (drawableFrame.getIntrinsicWidth() != width || drawableFrame.getIntrinsicHeight() != height) { @@ -492,8 +500,11 @@ public final class PointerIcon implements Parcelable { + "is different. All frames should have the exact same size and " + "share the same hotspot."); } - BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame; - mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame); + if (isVectorAnimation) { + drawableFrame = getBitmapDrawableFromVectorDrawable(resources, + (VectorDrawable) drawableFrame); + } + mBitmapFrames[i - 1] = getBitmapFromDrawable((BitmapDrawable) drawableFrame); } } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 22d34e6f7151..cbb3be7c4155 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9958,6 +9958,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide + */ + public void onGetCredentialException(String errorType, String errorMsg) { + if (getCredentialManagerCallback() == null) { + Log.w(AUTOFILL_LOG_TAG, "onGetCredentialException called but no callback found"); + return; + } + getCredentialManagerCallback().onError(new GetCredentialException(errorType, errorMsg)); + } + + /** * Gets the unique, logical identifier of this view in the activity, for autofill purposes. * * <p>The autofill id is created on demand, unless it is explicitly set by diff --git a/core/java/android/view/accessibility/IMagnificationConnection.aidl b/core/java/android/view/accessibility/IMagnificationConnection.aidl index aae51abd3c78..450cf7558c6f 100644 --- a/core/java/android/view/accessibility/IMagnificationConnection.aidl +++ b/core/java/android/view/accessibility/IMagnificationConnection.aidl @@ -124,4 +124,9 @@ oneway interface IMagnificationConnection { * @param scale magnification scale. */ void onUserMagnificationScaleChanged(int userId, int displayId, float scale); + + /** + * Notify the changes of fullscreen magnification activation on the specified display + */ + void onFullscreenMagnificationActivationChanged(int displayId, boolean activated); } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 364c94f7f92f..64e5a5bb87a2 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -50,6 +50,7 @@ import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.credentials.GetCredentialException; import android.credentials.GetCredentialResponse; import android.graphics.Rect; import android.metrics.LogMaker; @@ -105,6 +106,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.PrintWriter; +import java.io.Serializable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -2400,6 +2402,13 @@ public final class AutofillManager { final Bundle responseData = new Bundle(); responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result); + Serializable exception = data.getSerializableExtra( + CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, + GetCredentialException.class); + if (exception != null && Flags.autofillCredmanIntegration()) { + responseData.putSerializable( + CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, exception); + } final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE); if (newClientState != null) { responseData.putBundle(EXTRA_CLIENT_STATE, newClientState); @@ -2926,6 +2935,48 @@ public final class AutofillManager { } } + private void onGetCredentialException(int sessionId, AutofillId id, String errorType, + String errorMsg) { + synchronized (mLock) { + if (sessionId != mSessionId) { + Log.w(TAG, "onGetCredentialException afm sessionIds don't match"); + return; + } + + final AutofillClient client = getClient(); + if (client == null) { + Log.w(TAG, "onGetCredentialException afm client id null"); + return; + } + ArrayList<AutofillId> failedIds = new ArrayList<>(); + final View[] views = client.autofillClientFindViewsByAutofillIdTraversal( + Helper.toArray(new ArrayList<>(Collections.singleton(id)))); + if (views == null || views.length == 0) { + Log.w(TAG, "onGetCredentialException afm client view not found"); + return; + } + + final View view = views[0]; + if (view == null) { + Log.i(TAG, "onGetCredentialException View is null"); + + // Most likely view has been removed after the initial request was sent to the + // the service; this is fine, but we need to update the view status in the + // server side so it can be triggered again. + Log.d(TAG, "onGetCredentialException(): no View with id " + id); + failedIds.add(id); + } + if (id.isVirtualInt()) { + Log.i(TAG, "onGetCredentialException afm client id is virtual"); + // TODO(b/326314286): Handle virtual views + } else { + Log.i(TAG, "onGetCredentialException afm client id is NOT virtual"); + view.onGetCredentialException(errorType, errorMsg); + } + handleFailedIdsLocked(failedIds); + } + } + private void onGetCredentialResponse(int sessionId, AutofillId id, GetCredentialResponse response) { synchronized (mLock) { @@ -4382,6 +4433,15 @@ public final class AutofillManager { } @Override + public void onGetCredentialException(int sessionId, AutofillId id, + String errorType, String errorMsg) { + final AutofillManager afm = mAfm.get(); + if (afm != null) { + afm.post(() -> afm.onGetCredentialException(sessionId, id, errorType, errorMsg)); + } + } + + @Override public void autofillContent(int sessionId, AutofillId id, ClipData content) { final AutofillManager afm = mAfm.get(); if (afm != null) { diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index e838027a9ae1..904a7e0d6173 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -49,9 +49,12 @@ oneway interface IAutoFillManagerClient { void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values, boolean hideHighlight); - void onGetCredentialResponse(int sessionId, in AutofillId id, + void onGetCredentialResponse(int sessionId, in AutofillId id, in GetCredentialResponse response); + void onGetCredentialException(int sessionId, in AutofillId id, + in String errorType, in String errorMsg); + /** * Autofills the activity with rich content data (e.g. an image) from a dataset. */ diff --git a/core/java/android/window/flags/accessibility.aconfig b/core/java/android/window/flags/accessibility.aconfig index d467be6e5311..814c62017391 100644 --- a/core/java/android/window/flags/accessibility.aconfig +++ b/core/java/android/window/flags/accessibility.aconfig @@ -5,4 +5,11 @@ flag { namespace: "accessibility" description: "The flag controls whether the intersection check for non-magnifiable windows is needed when onWindowTransition," bug: "312624253" +} + +flag { + name: "magnification_always_draw_fullscreen_border" + namespace: "accessibility" + description: "Always draw fullscreen orange border in fullscreen magnification" + bug: "291891390" }
\ No newline at end of file diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 4e8661652e85..63a2474e70c1 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -7,3 +7,10 @@ flag { bug: "320350734" is_fixed_read_only: true } + +flag { + name: "enable_desktop_windowing_mode" + namespace: "lse_desktop_experience" + description: "Enables desktop windowing" + bug: "304778354" +} diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 285dee364bb0..b39d5cf3842b 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -85,9 +85,24 @@ static void android_view_MotionEvent_setNativePtr(JNIEnv* env, ScopedLocalRef<jo ScopedLocalRef<jobject> android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent& event) { - std::unique_ptr<MotionEvent> destEvent = std::make_unique<MotionEvent>(); + ScopedLocalRef<jobject> eventObj(env, + env->CallStaticObjectMethod(gMotionEventClassInfo.clazz, + gMotionEventClassInfo.obtain)); + if (env->ExceptionCheck() || !eventObj.get()) { + ALOGE("An exception occurred while obtaining a motion event."); + LOGE_EX(env); + env->ExceptionClear(); + return ScopedLocalRef<jobject>(env); + } + + MotionEvent* destEvent = android_view_MotionEvent_getNativePtr(env, eventObj.get()); + if (!destEvent) { + destEvent = new MotionEvent(); + android_view_MotionEvent_setNativePtr(env, eventObj, destEvent); + } + destEvent->copyFrom(&event, true); - return android_view_MotionEvent_obtainFromNative(env, std::move(destEvent)); + return eventObj; } ScopedLocalRef<jobject> android_view_MotionEvent_obtainFromNative( diff --git a/core/res/Android.bp b/core/res/Android.bp index 277824cbb4c8..e2e419fcf5b7 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -159,7 +159,7 @@ android_app { "android.content.pm.flags-aconfig", "android.provider.flags-aconfig", "camera_platform_flags", - "com.android.net.flags-aconfig", + "android.net.platform.flags-aconfig", "com.android.window.flags.window-aconfig", ], } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8720f947bb8d..900f9cb67718 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2331,12 +2331,12 @@ <!-- Allows system apps to call methods to register itself as a mDNS offload engine. <p>Not for use by third-party or privileged applications. @SystemApi - @FlaggedApi("com.android.net.flags.register_nsd_offload_engine") + @FlaggedApi("android.net.platform.flags.register_nsd_offload_engine") @hide This should only be used by system apps. --> <permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE" android:protectionLevel="signature" - android:featureFlag="com.android.net.flags.register_nsd_offload_engine" /> + android:featureFlag="android.net.platform.flags.register_nsd_offload_engine" /> <!-- ======================================= --> <!-- Permissions for short range, peripheral networks --> diff --git a/core/res/res/drawable/pointer_wait_vector.xml b/core/res/res/drawable/pointer_wait_vector.xml new file mode 100644 index 000000000000..4de0690bc26b --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="utf-8"?> +<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> + <item android:drawable="@drawable/pointer_wait_vector_0" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_1" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_2" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_3" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_4" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_5" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_6" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_7" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_8" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_9" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_10" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_11" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_12" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_13" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_14" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_15" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_16" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_17" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_18" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_19" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_20" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_21" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_22" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_23" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_24" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_25" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_26" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_27" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_28" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_29" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_30" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_31" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_32" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_33" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_34" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_35" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_36" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_37" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_38" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_39" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_40" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_41" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_42" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_43" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_44" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_45" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_46" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_47" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_48" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_49" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_50" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_51" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_52" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_53" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_54" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_55" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_56" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_57" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_58" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_59" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_60" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_61" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_62" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_63" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_64" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_65" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_66" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_67" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_68" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_69" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_70" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_71" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_72" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_73" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_74" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_75" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_76" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_77" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_78" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_79" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_80" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_81" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_82" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_83" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_84" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_85" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_86" android:duration="16"/> + <item android:drawable="@drawable/pointer_wait_vector_87" android:duration="16"/> +</animation-list> diff --git a/core/res/res/drawable/pointer_wait_vector_0.xml b/core/res/res/drawable/pointer_wait_vector_0.xml new file mode 100644 index 000000000000..4bfbc9f08b1e --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_0.xml @@ -0,0 +1,27 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.116,18.654a1.78,1.78 0,1 0,0.061 3.561,1.78 1.78,0 0,0 -0.061,-3.561m-7.194,-1.73a1.781,1.781 0,1 0,2.594 2.441,1.781 1.781,0 0,0 -2.594,-2.441" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.294,7.294a1.78,1.78 0,1 0,-2.517 -2.519,1.78 1.78,0 0,0 2.517,2.519" + android:fillColor="#FFF"/> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.225,7.294a1.78,1.78 0,1 0,-2.517 -2.519,1.78 1.78,0 0,0 2.517,2.519" + android:fillColor="#FFF"/> + <path + android:pathData="M20.436,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.747,16.665a1.78,1.78 0,1 0,2.539 2.497,1.78 1.78,0 0,0 -2.539,-2.497" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_1.xml b/core/res/res/drawable/pointer_wait_vector_1.xml new file mode 100644 index 000000000000..bab91e1d9402 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_1.xml @@ -0,0 +1,21 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.174,18.653a1.782,1.782 0,1 0,0.094 3.562,1.782 1.782,0 0,0 -0.094,-3.562m-7.187,-1.637a1.782,1.782 0,1 0,2.627 2.407,1.782 1.782,0 0,0 -2.627,-2.407" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.294,7.294a1.78,1.78 0,1 0,-2.517 -2.519,1.78 1.78,0 0,0 2.517,2.519" + android:fillColor="#FFF"/> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.225,7.294a1.78,1.78 0,1 0,-2.517 -2.519,1.78 1.78,0 0,0 2.517,2.519m1.203,2.888a1.78,1.78 0,1 0,0.016 3.562,1.78 1.78,0 1,0 -0.016,-3.562m-3.641,6.441a1.782,1.782 0,1 0,2.564 2.475,1.782 1.782,0 0,0 -2.564,-2.475" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_10.xml b/core/res/res/drawable/pointer_wait_vector_10.xml new file mode 100644 index 000000000000..ae3e36093aaa --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_10.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.028,20.374m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.952,18.76m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12.066m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.957,6.113m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.779,3.566m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.7,5.78m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.42,11.485m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.462,17.423m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_11.xml b/core/res/res/drawable/pointer_wait_vector_11.xml new file mode 100644 index 000000000000..d2c014d1ee3d --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_11.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.101,20.364m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.101,18.868m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12.074m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.931,6.139m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.706,3.569m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.646,5.731m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.41,11.338m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.509,17.366m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_12.xml b/core/res/res/drawable/pointer_wait_vector_12.xml new file mode 100644 index 000000000000..e9a36c033ca6 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_12.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.247,20.344m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.222,18.953m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.564,12.147m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.88,6.193m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.632,3.571m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.591,5.681m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.404,11.265m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.602,17.252m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_13.xml b/core/res/res/drawable/pointer_wait_vector_13.xml new file mode 100644 index 000000000000..5800f39fa7ef --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_13.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.392,20.321m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.344,19.035m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.565,12.184m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.83,6.246m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.559,3.575m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.535,5.633m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.39,11.118m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.693,17.136m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_14.xml b/core/res/res/drawable/pointer_wait_vector_14.xml new file mode 100644 index 000000000000..3566aaf358e7 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_14.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.537,20.295m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.467,19.115m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.567,12.258m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.755,6.327m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.485,3.579m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.423,5.537m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.386,11.082m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.759,17.048m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_15.xml b/core/res/res/drawable/pointer_wait_vector_15.xml new file mode 100644 index 000000000000..c995faf24f33 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_15.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.682,20.267m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.718,19.269m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.568,12.295m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.73,6.355m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.412,3.584m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.367,5.49m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.369,10.935m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.847,16.929m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_16.xml b/core/res/res/drawable/pointer_wait_vector_16.xml new file mode 100644 index 000000000000..179328205776 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_16.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.826,20.237m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.846,19.343m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.57,12.331m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.706,6.382m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.338,3.589m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.31,5.444m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.354,10.826m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.91,16.839m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_17.xml b/core/res/res/drawable/pointer_wait_vector_17.xml new file mode 100644 index 000000000000..59772f566644 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_17.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.969,20.204m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.974,19.414m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.571,12.368m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.681,6.41m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.191,3.602m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.194,5.352m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.333,10.68m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.994,16.718m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_18.xml b/core/res/res/drawable/pointer_wait_vector_18.xml new file mode 100644 index 000000000000..1bbb242d5663 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_18.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M14.184,20.149m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.236,19.55m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.575,12.442m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.585,6.521m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.118,3.61m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.136,5.307m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.315,10.571m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.075,16.595m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_19.xml b/core/res/res/drawable/pointer_wait_vector_19.xml new file mode 100644 index 000000000000..b8e0dd54708f --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_19.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M14.325,20.11m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.434,19.646m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.581,12.552m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.537,6.577m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.972,3.626m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.959,5.175m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.282,10.39m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.193,16.408m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_2.xml b/core/res/res/drawable/pointer_wait_vector_2.xml new file mode 100644 index 000000000000..5c4606f36569 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_2.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.331,20.43m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.355,18.269m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.035m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.435,11.89m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.094,17.834m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_20.xml b/core/res/res/drawable/pointer_wait_vector_20.xml new file mode 100644 index 000000000000..b3ac2dab2537 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_20.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M14.607,20.024m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.703,19.766m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.592,12.699m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.49,6.634m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.899,3.636m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.899,5.132m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.252,10.246m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.269,16.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_21.xml b/core/res/res/drawable/pointer_wait_vector_21.xml new file mode 100644 index 000000000000..884476e929a6 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_21.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M14.816,19.953m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.908,19.849m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.589,12.662m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.443,6.691m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.753,3.656m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.718,5.006m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.22,10.102m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.414,16.025m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_22.xml b/core/res/res/drawable/pointer_wait_vector_22.xml new file mode 100644 index 000000000000..20660ca89f3a --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_22.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M15.092,19.849m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.184,19.952m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.617,12.955m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.352,6.806m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.608,3.679m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.595,4.925m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.149,9.816m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.483,15.896m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_23.xml b/core/res/res/drawable/pointer_wait_vector_23.xml new file mode 100644 index 000000000000..6b594ee78eae --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_23.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M15.296,19.766m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.533,20.068m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.609,12.882m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.262,6.923m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.463,3.705m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.471,4.845m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.11,9.675m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.614,15.632m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_24.xml b/core/res/res/drawable/pointer_wait_vector_24.xml new file mode 100644 index 000000000000..a35203e3857b --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_24.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M15.632,19.615m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.816,20.149m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.626,13.028m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.175,7.041m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.318,3.733m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.346,4.769m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.046,9.463m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.677,15.498m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_25.xml b/core/res/res/drawable/pointer_wait_vector_25.xml new file mode 100644 index 000000000000..df8549af4006 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_25.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M15.896,19.483m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.174,20.236m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.645,13.174m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.089,7.161m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.174,3.763m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.219,4.694m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.977,9.253m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.794,15.228m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_26.xml b/core/res/res/drawable/pointer_wait_vector_26.xml new file mode 100644 index 000000000000..0abb7d41a59a --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_26.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M16.154,19.343m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.535,20.308m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.667,13.32m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.006,7.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.959,3.814m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.961,4.551m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.902,9.045m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.902,14.955m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_27.xml b/core/res/res/drawable/pointer_wait_vector_27.xml new file mode 100644 index 000000000000..bde242548af5 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_27.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M16.471,19.155m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.972,20.374m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.691,13.465m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.885,7.467m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.817,3.851m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.831,4.483m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.85,8.908m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.023,14.607m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_28.xml b/core/res/res/drawable/pointer_wait_vector_28.xml new file mode 100644 index 000000000000..19cb410b1405 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_28.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M16.778,18.953m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.411,20.416m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.718,13.61m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.807,7.592m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.604,3.911m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.566,4.354m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.677,8.502m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.109,14.325m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_29.xml b/core/res/res/drawable/pointer_wait_vector_29.xml new file mode 100644 index 000000000000..5d6776fcdb21 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_29.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17.136,18.693m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.926,20.436m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.748,13.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.731,7.718m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.463,3.954m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.365,4.263m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.55,8.236m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.203,13.969m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_3.xml b/core/res/res/drawable/pointer_wait_vector_3.xml new file mode 100644 index 000000000000..8a19401bd401 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_3.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.368,20.429m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.437,18.343m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.94,6.009m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.435,11.853m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.119,17.807m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_30.xml b/core/res/res/drawable/pointer_wait_vector_30.xml new file mode 100644 index 000000000000..56dcdc228401 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_30.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17.479,18.415m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12.441,20.425m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.796,13.969m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.586,7.974m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.184,4.047m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.092,4.151m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.414,7.975m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.281,13.61m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_31.xml b/core/res/res/drawable/pointer_wait_vector_31.xml new file mode 100644 index 000000000000..5687a9b46b67 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_31.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17.807,18.12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M13.028,20.373m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.832,14.112m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.517,8.104m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.908,4.15m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M14.817,4.047m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.269,7.718m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.344,13.247m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_32.xml b/core/res/res/drawable/pointer_wait_vector_32.xml new file mode 100644 index 000000000000..cb626e41d1d5 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_32.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M18.17,17.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M13.61,20.281m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.87,14.254m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.417,8.302m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.704,4.234m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M14.537,3.954m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.076,7.405m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.397,12.809m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_33.xml b/core/res/res/drawable/pointer_wait_vector_33.xml new file mode 100644 index 000000000000..052f973f2ab4 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_33.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M18.51,17.366m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M14.183,20.149m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.954,14.537m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.323,8.501m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.435,4.354m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M14.184,3.851m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.868,7.101m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.428,12.368m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_34.xml b/core/res/res/drawable/pointer_wait_vector_34.xml new file mode 100644 index 000000000000..9e665756f4c2 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_34.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M18.911,16.839m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M14.885,19.928m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.999,14.677m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.206,8.771m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.17,4.483m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M13.826,3.764m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.603,6.748m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.433,11.779m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_35.xml b/core/res/res/drawable/pointer_wait_vector_35.xml new file mode 100644 index 000000000000..1d063fc9fbb9 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_35.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M19.232,16.345m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.566,19.646m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.098,14.955m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.098,9.045m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.846,4.657m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M13.465,3.692m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.27,6.355m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.41,11.338m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_36.xml b/core/res/res/drawable/pointer_wait_vector_36.xml new file mode 100644 index 000000000000..db0ad1c847bc --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_36.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M19.583,15.698m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.345,19.232m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.205,15.229m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.999,9.323m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.592,4.807m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M13.028,3.626m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.035m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.344,10.753m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_37.xml b/core/res/res/drawable/pointer_wait_vector_37.xml new file mode 100644 index 000000000000..ee944a6ccbe6 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_37.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M19.876,15.023m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.077,18.738m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.323,15.499m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.911,9.604m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.221,5.047m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12.589,3.584m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.59,5.681m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.236,10.174m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_38.xml b/core/res/res/drawable/pointer_wait_vector_38.xml new file mode 100644 index 000000000000..a347a36e34b0 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_38.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M20.129,14.255m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.861,18.069m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.45,15.764m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.796,10.031m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.864,5.307m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12.147,3.565m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.136,5.307m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.046,9.463m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_39.xml b/core/res/res/drawable/pointer_wait_vector_39.xml new file mode 100644 index 000000000000..2c86f0dd7849 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_39.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M20.321,13.392m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.557,17.309m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.586,16.026m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.705,10.462m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.521,5.585m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.559,3.575m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.657,4.965m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.766,8.704m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_4.xml b/core/res/res/drawable/pointer_wait_vector_4.xml new file mode 100644 index 000000000000..a6690c003c4b --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_4.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.441,20.425m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.493,18.391m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.914,5.983m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.433,11.772m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.17,17.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_40.xml b/core/res/res/drawable/pointer_wait_vector_40.xml new file mode 100644 index 000000000000..953fdc1deb23 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_40.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M20.428,12.368m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.232,16.345m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.731,16.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.656,10.753m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.193,5.88m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.972,3.626m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.026,4.586m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.449,8.039m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_41.xml b/core/res/res/drawable/pointer_wait_vector_41.xml new file mode 100644 index 000000000000..a1b17fa2d19d --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_41.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M20.41,11.338m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.766,15.296m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.924,16.595m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.602,11.191m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.83,6.246m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.39,3.719m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.364,4.263m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.994,7.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_42.xml b/core/res/res/drawable/pointer_wait_vector_42.xml new file mode 100644 index 000000000000..4de9b8d14dd8 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_42.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M20.244,10.21m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.14,14.219m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.153,16.929m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.571,11.632m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.49,6.634m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.817,3.851m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M14.677,4m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.367,6.465m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_43.xml b/core/res/res/drawable/pointer_wait_vector_43.xml new file mode 100644 index 000000000000..38187fe62ac4 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_43.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M19.94,9.149m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.365,13.101m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.397,17.252m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.566,12.221m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.089,7.161m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.115,4.072m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M13.826,3.763m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.645,5.73m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_44.xml b/core/res/res/drawable/pointer_wait_vector_44.xml new file mode 100644 index 000000000000..890ba7ee6533 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_44.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M19.466,8.072m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.436,12.073m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.73,17.645m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.589,12.662m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.768,7.655m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.368,4.385m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12.882,3.61m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.839,5.089m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_45.xml b/core/res/res/drawable/pointer_wait_vector_45.xml new file mode 100644 index 000000000000..309ac082edef --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_45.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M18.847,7.071m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.382,11.045m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.139,18.069m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.656,13.247m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.417,8.302m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.592,4.807m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.853,3.565m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.83,4.483m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_46.xml b/core/res/res/drawable/pointer_wait_vector_46.xml new file mode 100644 index 000000000000..5a9bf706064a --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_46.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M18.195,6.273m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.237,10.174m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.465,18.367m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.797,13.969m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.124,8.977m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.864,5.307m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.826,3.646m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M14.747,4.023m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_47.xml b/core/res/res/drawable/pointer_wait_vector_47.xml new file mode 100644 index 000000000000..0068f27c78d5 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_47.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17.451,5.561m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.046,9.463m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.922,18.738m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.954,14.537m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.851,9.817m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.087,5.983m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.675,3.89m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M13.61,3.718m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_48.xml b/core/res/res/drawable/pointer_wait_vector_48.xml new file mode 100644 index 000000000000..6ece990ad516 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_48.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M16.748,5.026m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.822,8.84m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.405,19.076m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.205,15.229m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.667,10.68m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.397,6.748m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.569,4.293m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12.588,3.584m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_49.xml b/core/res/res/drawable/pointer_wait_vector_49.xml new file mode 100644 index 000000000000..d44438975224 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_49.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M16.09,4.621m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.583,8.302m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.974,19.414m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.586,16.025m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.571,11.632m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.768,7.655m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.655,4.768m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.558,3.575m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_5.xml b/core/res/res/drawable/pointer_wait_vector_5.xml new file mode 100644 index 000000000000..4952fe57397b --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_5.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.515,20.421m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.577,18.463m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.877,5.947m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.433,11.779m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.22,17.7m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_50.xml b/core/res/res/drawable/pointer_wait_vector_50.xml new file mode 100644 index 000000000000..a85155fc5dd2 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_50.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M15.499,4.323m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.343,7.846m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.636,19.737m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.047,16.778m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.589,12.662m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.234,8.704m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.634,5.49m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.608,3.679m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_51.xml b/core/res/res/drawable/pointer_wait_vector_51.xml new file mode 100644 index 000000000000..95b4aaa2365a --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_51.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M14.885,4.072m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.155,7.529m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.393,20.024m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.585,17.479m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.748,13.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.851,9.817m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.88,6.193m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.816,3.851m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_52.xml b/core/res/res/drawable/pointer_wait_vector_52.xml new file mode 100644 index 000000000000..34619b5d4361 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_52.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M14.396,3.911m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.953,7.221m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.246,20.252m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.3,18.22m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.072,14.885m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.636,10.899m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.307,6.864m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.114,4.072m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_53.xml b/core/res/res/drawable/pointer_wait_vector_53.xml new file mode 100644 index 000000000000..926b63998470 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_53.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.969,3.797m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.782,6.982m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.118,20.39m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.221,18.953m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.551,15.961m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.564,11.927m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.807,7.592m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.568,4.293m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_54.xml b/core/res/res/drawable/pointer_wait_vector_54.xml new file mode 100644 index 000000000000..c51851fce837 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_54.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.61,3.718m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.603,6.748m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12.147,20.435m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.17,19.517m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.132,16.899m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.618,12.955m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.45,8.236m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.104,4.517m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_55.xml b/core/res/res/drawable/pointer_wait_vector_55.xml new file mode 100644 index 000000000000..cdf435fa0637 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_55.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.247,3.656m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.463,6.577m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M13.247,20.344m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.253,19.977m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.83,17.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.748,13.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.178,8.84m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.686,4.749m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_56.xml b/core/res/res/drawable/pointer_wait_vector_56.xml new file mode 100644 index 000000000000..1528711e2542 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_56.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M13.028,3.626m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.319,6.41m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M14.325,20.11m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.39,20.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.521,18.415m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.954,14.537m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.976,9.393m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.405,4.924m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_57.xml b/core/res/res/drawable/pointer_wait_vector_57.xml new file mode 100644 index 000000000000..1715db1e2bef --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_57.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.772,3.599m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.22,6.3m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.431,19.707m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.411,20.416m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.282,18.994m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.178,15.16m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.851,9.817m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.041,5.175m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_58.xml b/core/res/res/drawable/pointer_wait_vector_58.xml new file mode 100644 index 000000000000..83aec4b24851 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_58.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.589,3.584m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.12,6.193m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.471,19.155m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12.442,20.425m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M7.974,19.414m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.417,15.698m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.748,10.246m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.864,5.307m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_59.xml b/core/res/res/drawable/pointer_wait_vector_59.xml new file mode 100644 index 000000000000..2e5e4728ec85 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_59.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.442,3.575m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.069,6.139m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.309,18.556m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M13.465,20.308m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M8.568,19.707m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.657,16.154m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.679,10.608m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.634,5.49m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_6.xml b/core/res/res/drawable/pointer_wait_vector_6.xml new file mode 100644 index 000000000000..d4f57af49b40 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_6.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.588,20.416m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.633,18.51m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.926,3.564m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.861,5.931m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.431,11.706m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.269,17.645m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_60.xml b/core/res/res/drawable/pointer_wait_vector_60.xml new file mode 100644 index 000000000000..4f257671d25d --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_60.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.294,3.569m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.069,6.139m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.12,17.807m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M14.183,20.149m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.115,19.928m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M4.845,16.471m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.646,10.826m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.465,5.633m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_61.xml b/core/res/res/drawable/pointer_wait_vector_61.xml new file mode 100644 index 000000000000..7e06db4d1874 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_61.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.147,3.565m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.017,6.087m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.738,17.077m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M14.885,19.928m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M9.604,20.089m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.047,16.779m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.61,11.118m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.355,5.73m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_62.xml b/core/res/res/drawable/pointer_wait_vector_62.xml new file mode 100644 index 000000000000..b3d0541a6417 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_62.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.074,3.564m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.231,16.345m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.432,19.707m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.102,20.22m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.307,17.136m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.584,11.411m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.246,5.83m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_63.xml b/core/res/res/drawable/pointer_wait_vector_63.xml new file mode 100644 index 000000000000..ee09c2537d3f --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_63.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.583,15.698m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M15.961,19.449m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.39,20.282m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.397,17.252m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.575,11.558m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.193,5.88m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_64.xml b/core/res/res/drawable/pointer_wait_vector_64.xml new file mode 100644 index 000000000000..e87f94f33304 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_64.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M19.822,15.16m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.345,19.232m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.826,20.354m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.537,17.423m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.571,11.632m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.139,5.931m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_65.xml b/core/res/res/drawable/pointer_wait_vector_65.xml new file mode 100644 index 000000000000..b8407c448db5 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_65.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.023,14.607m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.718,18.994m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M10.972,20.374m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.681,17.59m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.569,11.706m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.087,5.983m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_66.xml b/core/res/res/drawable/pointer_wait_vector_66.xml new file mode 100644 index 000000000000..7e2448df4d11 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_66.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.149,14.184m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M16.959,18.825m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.265,20.405m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.78,17.7m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.565,11.853m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_67.xml b/core/res/res/drawable/pointer_wait_vector_67.xml new file mode 100644 index 000000000000..615ac04c40b3 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_67.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.252,13.754m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.194,18.648m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.412,20.416m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.88,17.807m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_68.xml b/core/res/res/drawable/pointer_wait_vector_68.xml new file mode 100644 index 000000000000..0ba4634a131f --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_68.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.308,13.465m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.366,18.51m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.632,20.429m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.931,17.861m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_69.xml b/core/res/res/drawable/pointer_wait_vector_69.xml new file mode 100644 index 000000000000..efa90bf9df22 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_69.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.354,13.174m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.535,18.367m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.706,20.432m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.931,17.86m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_7.xml b/core/res/res/drawable/pointer_wait_vector_7.xml new file mode 100644 index 000000000000..86d571a40f73 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_7.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.661,20.411m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.69,18.556m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.024,6.045m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.889,3.564m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.808,5.88m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.428,11.632m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.318,17.59m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_70.xml b/core/res/res/drawable/pointer_wait_vector_70.xml new file mode 100644 index 000000000000..4e58af5dfb3b --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_70.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.39,12.882m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.645,18.27m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.853,20.435m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_71.xml b/core/res/res/drawable/pointer_wait_vector_71.xml new file mode 100644 index 000000000000..5a534342ec8d --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_71.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.416,12.589m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.754,18.17m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.853,20.435m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_72.xml b/core/res/res/drawable/pointer_wait_vector_72.xml new file mode 100644 index 000000000000..40fb5d313f7c --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_72.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.425,12.442m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.807,18.12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_73.xml b/core/res/res/drawable/pointer_wait_vector_73.xml new file mode 100644 index 000000000000..933cc6ae4813 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_73.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.429,12.368m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.861,18.069m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_74.xml b/core/res/res/drawable/pointer_wait_vector_74.xml new file mode 100644 index 000000000000..6c7fef264f81 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_74.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.431,12.294m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_75.xml b/core/res/res/drawable/pointer_wait_vector_75.xml new file mode 100644 index 000000000000..24e64e3491cf --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_75.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.435,12.147m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_76.xml b/core/res/res/drawable/pointer_wait_vector_76.xml new file mode 100644 index 000000000000..2ede57de05fd --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_76.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.436,12.073m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_77.xml b/core/res/res/drawable/pointer_wait_vector_77.xml new file mode 100644 index 000000000000..71f811a497a0 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_77.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.436,12.037m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_78.xml b/core/res/res/drawable/pointer_wait_vector_78.xml new file mode 100644 index 000000000000..e2d56c6f676e --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_78.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.437,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_79.xml b/core/res/res/drawable/pointer_wait_vector_79.xml new file mode 100644 index 000000000000..e2d56c6f676e --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_79.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.437,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_8.xml b/core/res/res/drawable/pointer_wait_vector_8.xml new file mode 100644 index 000000000000..ebac74961497 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_8.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.808,20.398m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.806,18.648m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.008,6.061m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.853,3.565m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.754,5.83m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.425,11.558m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.367,17.535m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_80.xml b/core/res/res/drawable/pointer_wait_vector_80.xml new file mode 100644 index 000000000000..40fb5d313f7c --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_80.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.425,12.442m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.807,18.12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_81.xml b/core/res/res/drawable/pointer_wait_vector_81.xml new file mode 100644 index 000000000000..933cc6ae4813 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_81.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.429,12.368m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.861,18.069m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_82.xml b/core/res/res/drawable/pointer_wait_vector_82.xml new file mode 100644 index 000000000000..6c7fef264f81 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_82.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.431,12.294m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_83.xml b/core/res/res/drawable/pointer_wait_vector_83.xml new file mode 100644 index 000000000000..24e64e3491cf --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_83.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.435,12.147m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_84.xml b/core/res/res/drawable/pointer_wait_vector_84.xml new file mode 100644 index 000000000000..2ede57de05fd --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_84.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.436,12.073m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_85.xml b/core/res/res/drawable/pointer_wait_vector_85.xml new file mode 100644 index 000000000000..71f811a497a0 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_85.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.436,12.037m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_86.xml b/core/res/res/drawable/pointer_wait_vector_86.xml new file mode 100644 index 000000000000..e2d56c6f676e --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_86.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.437,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_87.xml b/core/res/res/drawable/pointer_wait_vector_87.xml new file mode 100644 index 000000000000..e2d56c6f676e --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_87.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,3.563m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.437,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.966,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M12,20.437m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,17.966m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.034,6.034m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_9.xml b/core/res/res/drawable/pointer_wait_vector_9.xml new file mode 100644 index 000000000000..d46dfa394c01 --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_9.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.881,20.39m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M6.864,18.693m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M3.563,12.052m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M5.983,6.087m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M11.816,3.565m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M17.754,5.83m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M20.423,11.522m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> + <path + android:pathData="M18.415,17.479m-1.781,0a1.781,1.781 0,1 1,3.562 0a1.781,1.781 0,1 1,-3.562 0" + android:fillColor="#FFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_wait_vector_icon.xml b/core/res/res/drawable/pointer_wait_vector_icon.xml new file mode 100644 index 000000000000..45d371e95c5d --- /dev/null +++ b/core/res/res/drawable/pointer_wait_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_wait_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 104b7cd5450b..c87b7cdb1e8e 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -73,6 +73,11 @@ <bool name="auto_data_switch_ping_test_before_switch">true</bool> <java-symbol type="bool" name="auto_data_switch_ping_test_before_switch" /> + <!-- TODO: remove after V --> + <!-- Boolean indicating whether allow to use a roaming nonDDS if user enabled its roaming. --> + <bool name="auto_data_switch_allow_roaming">true</bool> + <java-symbol type="bool" name="auto_data_switch_allow_roaming" /> + <!-- Define the tolerated gap of score for auto data switch decision, larger than which the device will switch to the SIM with higher score. The score is used in conjunction with the score table defined in diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index adf8d9f95b52..5945f81704c5 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1468,7 +1468,7 @@ please see styles_device_defaults.xml. <item name="pointerIconHand">@drawable/pointer_hand_vector_icon</item> <item name="pointerIconContextMenu">@drawable/pointer_context_menu_vector_icon</item> <item name="pointerIconHelp">@drawable/pointer_help_vector_icon</item> - <item name="pointerIconWait">@drawable/pointer_wait_icon</item> + <item name="pointerIconWait">@drawable/pointer_wait_vector_icon</item> <item name="pointerIconCell">@drawable/pointer_cell_vector_icon</item> <item name="pointerIconCrosshair">@drawable/pointer_crosshair_vector_icon</item> <item name="pointerIconText">@drawable/pointer_text_vector_icon</item> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 8d6ddf296c99..37e6780a8109 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -28,19 +28,10 @@ filegroup { visibility: ["//visibility:private"], } -java_defaults { - name: "FrameworksCoreTests-resources", - aaptflags: [ - "-0 .dat", - "-0 .gld", - "-c fa", - ], - resource_dirs: ["res"], -} - android_test { name: "FrameworksCoreTests", - defaults: ["FrameworksCoreTests-resources"], + // FrameworksCoreTestsRavenwood references the .aapt.srcjar + use_resource_processor: false, srcs: [ "src/**/*.java", @@ -126,6 +117,7 @@ android_test { certificate: "platform", + resource_dirs: ["res"], resource_zips: [":FrameworksCoreTests_apks_as_resources"], java_resources: [":FrameworksCoreTests_unit_test_cert_der"], @@ -136,22 +128,6 @@ android_test { ], } -// FrameworksCoreTestsRavenwood pulls in the R.java class from this one. -// Note, "FrameworksCoreTests" and "FrameworksCoreTests-resonly" _might_ not have indentical -// R.java (not sure if there's a guarantee), but that doesn't matter as long as -// FrameworksCoreTestsRavenwood consistently uses the R definition in this module. -android_app { - name: "FrameworksCoreTests-resonly", - defaults: ["FrameworksCoreTests-resources"], - - // FrameworksCoreTestsRavenwood references the .aapt.srcjar - use_resource_processor: false, - libs: [ - "framework-res", - ], - sdk_version: "core_platform", -} - // Rules to copy all the test apks to the intermediate raw resource directory java_genrule { name: "FrameworksCoreTests_apks_as_resources", @@ -249,11 +225,7 @@ android_ravenwood_test { "src/com/android/internal/util/**/*.java", "src/com/android/internal/power/EnergyConsumerStatsTest.java", - // Pull in R.java from FrameworksCoreTests-resonly, not from FrameworksCoreTests, - // to avoid having a dependency to FrameworksCoreTests. - // This way, when updating source files and running this test, we don't need to - // rebuild the entire FrameworksCoreTests, which would be slow. - ":FrameworksCoreTests-resonly{.aapt.srcjar}", + ":FrameworksCoreTests{.aapt.srcjar}", ":FrameworksCoreTests-aidl", ":FrameworksCoreTests-helpers", ":FrameworksCoreTestDoubles-sources", diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index a7d083cbc399..d95834fc0f4a 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -65,6 +65,7 @@ import android.util.DisplayMetrics; import android.util.MergedConfiguration; import android.view.Display; import android.view.View; +import android.window.ActivityWindowInfo; import android.window.WindowContextInfo; import android.window.WindowTokenClientController; @@ -839,7 +840,7 @@ public class ActivityThreadTest { final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain( activity.getActivityToken(), null, null, 0, new MergedConfiguration(currentConfig, currentConfig), - false /* preserveWindow */); + false /* preserveWindow */, new ActivityWindowInfo()); final ResumeActivityItem resumeStateRequest = ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */, false /* shouldSendCompatFakeFocus*/); diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java index b5e8203c51ed..30545f994f01 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java @@ -40,6 +40,7 @@ import android.util.ArrayMap; import android.util.MergedConfiguration; import android.view.IWindow; import android.view.InsetsState; +import android.window.ActivityWindowInfo; import android.window.ClientWindowFrames; import android.window.WindowContext; import android.window.WindowContextInfo; @@ -116,7 +117,8 @@ public class ClientTransactionItemTest { public void testActivityRelaunchItem_getContextToUpdate() { final ActivityRelaunchItem item = ActivityRelaunchItem .obtain(mActivityToken, null /* pendingResults */, null /* pendingNewIntents */, - 0 /* configChange */, mMergedConfiguration, false /* preserveWindow */); + 0 /* configChange */, mMergedConfiguration, false /* preserveWindow */, + new ActivityWindowInfo()); final Context context = item.getContextToUpdate(mHandler); assertEquals(mActivity, context); diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 207fe730aac2..a8466bb092c8 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -151,7 +151,8 @@ public class ObjectPoolTests { @Test public void testRecycleActivityRelaunchItem() { testRecycle(() -> ActivityRelaunchItem.obtain(mActivityToken, - resultInfoList(), referrerIntentList(), 42, mergedConfig(), true)); + resultInfoList(), referrerIntentList(), 42, mergedConfig(), true, + new ActivityWindowInfo())); } @Test diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 03b85dc8b4e2..9743e84b9349 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -220,8 +220,11 @@ public class TransactionParcelTests { // Write to parcel Configuration overrideConfig = new Configuration(); overrideConfig.assetsSeq = 5; + final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); + activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000), + new Rect(0, 0, 500, 500)); ActivityRelaunchItem item = ActivityRelaunchItem.obtain(mActivityToken, resultInfoList(), - referrerIntentList(), 35, mergedConfig(), true); + referrerIntentList(), 35, mergedConfig(), true, activityWindowInfo); writeAndPrepareForReading(item); // Read from parcel and assert diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java index 65955b1d9bcc..e37dea4dfd69 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java @@ -126,7 +126,7 @@ public final class CommonFoldingFeature { * @see #FEATURE_PATTERN * @return {@link List} of {@link CommonFoldingFeature}. */ - static List<CommonFoldingFeature> parseListFromString(@NonNull String value, + public static List<CommonFoldingFeature> parseListFromString(@NonNull String value, @State int hingeState) { List<CommonFoldingFeature> features = new ArrayList<>(); String[] featureStrings = value.split(";"); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java index a184dff5005b..88fd461debbe 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java @@ -36,6 +36,7 @@ import androidx.window.util.BaseDataProducer; import com.android.internal.R; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -78,7 +79,9 @@ public final class DeviceStateManagerFoldingFeatureProducer private int mCurrentBaseDeviceState = INVALID_DEVICE_STATE; @NonNull - private final BaseDataProducer<String> mRawFoldSupplier; + private final RawFoldingFeatureProducer mRawFoldSupplier; + + private final boolean mIsHalfOpenedSupported; private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() { @Override @@ -101,10 +104,12 @@ public final class DeviceStateManagerFoldingFeatureProducer }; public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context, - @NonNull BaseDataProducer<String> rawFoldSupplier) { + @NonNull RawFoldingFeatureProducer rawFoldSupplier, + @NonNull DeviceStateManager deviceStateManager) { mRawFoldSupplier = rawFoldSupplier; String[] deviceStatePosturePairs = context.getResources() .getStringArray(R.array.config_device_state_postures); + boolean isHalfOpenedSupported = false; for (String deviceStatePosturePair : deviceStatePosturePairs) { String[] deviceStatePostureMapping = deviceStatePosturePair.split(":"); if (deviceStatePostureMapping.length != 2) { @@ -128,12 +133,13 @@ public final class DeviceStateManagerFoldingFeatureProducer } continue; } - + isHalfOpenedSupported = isHalfOpenedSupported + || posture == CommonFoldingFeature.COMMON_STATE_HALF_OPENED; mDeviceStateToPostureMap.put(deviceState, posture); } - + mIsHalfOpenedSupported = isHalfOpenedSupported; if (mDeviceStateToPostureMap.size() > 0) { - Objects.requireNonNull(context.getSystemService(DeviceStateManager.class)) + Objects.requireNonNull(deviceStateManager) .registerCallback(context.getMainExecutor(), mDeviceStateCallback); } } @@ -188,6 +194,31 @@ public final class DeviceStateManagerFoldingFeatureProducer } /** + * Returns a {@link List} of all the {@link CommonFoldingFeature} with the state set to + * {@link CommonFoldingFeature#COMMON_STATE_UNKNOWN}. This method parses a {@link String} so a + * caller should consider caching the value or the derived value. + */ + @NonNull + public List<CommonFoldingFeature> getFoldsWithUnknownState() { + Optional<String> optionalFoldingFeatureString = mRawFoldSupplier.getCurrentData(); + + if (optionalFoldingFeatureString.isPresent()) { + return CommonFoldingFeature.parseListFromString( + optionalFoldingFeatureString.get(), CommonFoldingFeature.COMMON_STATE_UNKNOWN + ); + } + return Collections.emptyList(); + } + + + /** + * Returns {@code true} if the device supports half-opened mode, {@code false} otherwise. + */ + public boolean isHalfOpenedSupported() { + return mIsHalfOpenedSupported; + } + + /** * Adds the data to the storeFeaturesConsumer when the data is ready. * @param storeFeaturesConsumer a consumer to collect the data when it is first available. */ diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 29cf05407a61..6714263ad952 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -20,6 +20,7 @@ import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.Application; import android.content.Context; +import android.hardware.devicestate.DeviceStateManager; import android.util.Log; import androidx.annotation.NonNull; @@ -64,6 +65,11 @@ public class WindowExtensionsImpl implements WindowExtensions { } @NonNull + private DeviceStateManager getDeviceStateManager() { + return Objects.requireNonNull(getApplication().getSystemService(DeviceStateManager.class)); + } + + @NonNull private DeviceStateManagerFoldingFeatureProducer getFoldingFeatureProducer() { if (mFoldingFeatureProducer == null) { synchronized (mLock) { @@ -73,7 +79,7 @@ public class WindowExtensionsImpl implements WindowExtensions { new RawFoldingFeatureProducer(context); mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context, - foldingFeatureProducer); + foldingFeatureProducer, getDeviceStateManager()); } } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/DisplayFoldFeatureUtil.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/DisplayFoldFeatureUtil.java new file mode 100644 index 000000000000..a0f481a911ad --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/DisplayFoldFeatureUtil.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.window.extensions.layout; + +import androidx.window.common.CommonFoldingFeature; +import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Util functions for working with {@link androidx.window.extensions.layout.DisplayFoldFeature}. + */ +public class DisplayFoldFeatureUtil { + + private DisplayFoldFeatureUtil() {} + + private static DisplayFoldFeature create(CommonFoldingFeature foldingFeature, + boolean isHalfOpenedSupported) { + final int foldType; + if (foldingFeature.getType() == CommonFoldingFeature.COMMON_TYPE_HINGE) { + foldType = DisplayFoldFeature.TYPE_HINGE; + } else { + foldType = DisplayFoldFeature.TYPE_SCREEN_FOLD_IN; + } + DisplayFoldFeature.Builder featureBuilder = new DisplayFoldFeature.Builder(foldType); + + if (isHalfOpenedSupported) { + featureBuilder.addProperty(DisplayFoldFeature.FOLD_PROPERTY_SUPPORTS_HALF_OPENED); + } + return featureBuilder.build(); + } + + /** + * Returns the list of supported {@link DisplayFeature} calculated from the + * {@link DeviceStateManagerFoldingFeatureProducer}. + */ + public static List<DisplayFoldFeature> extractDisplayFoldFeatures( + DeviceStateManagerFoldingFeatureProducer producer) { + List<DisplayFoldFeature> foldFeatures = new ArrayList<>(); + List<CommonFoldingFeature> folds = producer.getFoldsWithUnknownState(); + + final boolean isHalfOpenedSupported = producer.isHalfOpenedSupported(); + for (CommonFoldingFeature fold : folds) { + foldFeatures.add(DisplayFoldFeatureUtil.create(fold, isHalfOpenedSupported)); + } + return foldFeatures; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index 6e704f35fb22..4fd11c495529 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -45,7 +45,6 @@ import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.EmptyLifecycleCallbacksAdapter; import androidx.window.extensions.core.util.function.Consumer; -import androidx.window.util.DataProducer; import java.util.ArrayList; import java.util.Collections; @@ -56,10 +55,6 @@ import java.util.Set; /** * Reference implementation of androidx.window.extensions.layout OEM interface for use with * WindowManager Jetpack. - * - * NOTE: This version is a work in progress and under active development. It MUST NOT be used in - * production builds since the interface can still change before reaching stable version. - * Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead. */ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private static final String TAG = WindowLayoutComponentImpl.class.getSimpleName(); @@ -71,7 +66,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { new ArrayMap<>(); @GuardedBy("mLock") - private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer; + private final DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer; @GuardedBy("mLock") private final List<CommonFoldingFeature> mLastReportedFoldingFeatures = new ArrayList<>(); @@ -87,12 +82,17 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private final RawConfigurationChangedListener mRawConfigurationChangedListener = new RawConfigurationChangedListener(); + private final SupportedWindowFeatures mSupportedWindowFeatures; + public WindowLayoutComponentImpl(@NonNull Context context, @NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) { ((Application) context.getApplicationContext()) .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); mFoldingFeatureProducer = foldingFeatureProducer; mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); + final List<DisplayFoldFeature> displayFoldFeatures = + DisplayFoldFeatureUtil.extractDisplayFoldFeatures(mFoldingFeatureProducer); + mSupportedWindowFeatures = new SupportedWindowFeatures.Builder(displayFoldFeatures).build(); } /** @@ -283,6 +283,15 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } } + /** + * Returns the {@link SupportedWindowFeatures} for the device. This list does not change over + * time. + */ + @NonNull + public SupportedWindowFeatures getSupportedWindowFeatures() { + return mSupportedWindowFeatures; + } + /** @see #getWindowLayoutInfo(Context, List) */ private WindowLayoutInfo getWindowLayoutInfo(int displayId, @NonNull WindowConfiguration windowConfiguration, diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java index a836e05b2d66..56c3bce87d6e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java @@ -17,6 +17,7 @@ package androidx.window.sidecar; import static android.view.Display.DEFAULT_DISPLAY; + import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation; import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; @@ -25,6 +26,7 @@ import android.app.ActivityThread; import android.app.Application; import android.content.Context; import android.graphics.Rect; +import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; import android.os.IBinder; @@ -49,10 +51,11 @@ class SampleSidecarImpl extends StubSidecar { SampleSidecarImpl(Context context) { ((Application) context.getApplicationContext()) .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); - BaseDataProducer<String> settingsFeatureProducer = new RawFoldingFeatureProducer(context); + RawFoldingFeatureProducer settingsFeatureProducer = new RawFoldingFeatureProducer(context); BaseDataProducer<List<CommonFoldingFeature>> foldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context, - settingsFeatureProducer); + settingsFeatureProducer, + context.getSystemService(DeviceStateManager.class)); foldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt new file mode 100644 index 000000000000..95d47146e834 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + +import com.android.internal.util.FrameworkStatsLog +import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.util.KtProtoLog + +/** + * Event logger for logging desktop mode session events + */ +class DesktopModeEventLogger { + /** + * Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for + * entering desktop mode + */ + fun logSessionEnter(sessionId: Int, enterReason: EnterReason) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging session enter, session: %s reason: %s", + sessionId, enterReason.name + ) + FrameworkStatsLog.write(DESKTOP_MODE_ATOM_ID, + /* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER, + /* enterReason */ enterReason.reason, + /* exitReason */ 0, + /* session_id */ sessionId) + } + + /** + * Logs the exit of desktop mode having session id [sessionId] and the reason [exitReason] for + * exiting desktop mode + */ + fun logSessionExit(sessionId: Int, exitReason: ExitReason) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging session exit, session: %s reason: %s", + sessionId, exitReason.name + ) + FrameworkStatsLog.write(DESKTOP_MODE_ATOM_ID, + /* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__EXIT, + /* enterReason */ 0, + /* exitReason */ exitReason.reason, + /* session_id */ sessionId) + } + + /** + * Logs that the task with update [taskUpdate] was added in the desktop mode session having + * session id [sessionId] + */ + fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging task added, session: %s taskId: %s", + sessionId, taskUpdate.instanceId + ) + FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID, + /* task_event */ + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED, + /* instance_id */ + taskUpdate.instanceId, + /* uid */ + taskUpdate.uid, + /* task_height */ + taskUpdate.taskHeight, + /* task_width */ + taskUpdate.taskWidth, + /* task_x */ + taskUpdate.taskX, + /* task_y */ + taskUpdate.taskY, + /* session_id */ + sessionId) + } + + /** + * Logs that the task with update [taskUpdate] was removed in the desktop mode session having + * session id [sessionId] + */ + fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging task remove, session: %s taskId: %s", + sessionId, taskUpdate.instanceId + ) + FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID, + /* task_event */ + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED, + /* instance_id */ + taskUpdate.instanceId, + /* uid */ + taskUpdate.uid, + /* task_height */ + taskUpdate.taskHeight, + /* task_width */ + taskUpdate.taskWidth, + /* task_x */ + taskUpdate.taskX, + /* task_y */ + taskUpdate.taskY, + /* session_id */ + sessionId) + } + + /** + * Logs that the task with update [taskUpdate] had it's info changed in the desktop mode session + * having session id [sessionId] + */ + fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging task info changed, session: %s taskId: %s", + sessionId, taskUpdate.instanceId + ) + FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID, + /* task_event */ + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED, + /* instance_id */ + taskUpdate.instanceId, + /* uid */ + taskUpdate.uid, + /* task_height */ + taskUpdate.taskHeight, + /* task_width */ + taskUpdate.taskWidth, + /* task_x */ + taskUpdate.taskX, + /* task_y */ + taskUpdate.taskY, + /* session_id */ + sessionId) + } + + companion object { + data class TaskUpdate( + val instanceId: Int, + val uid: Int, + val taskHeight: Int = Int.MIN_VALUE, + val taskWidth: Int = Int.MIN_VALUE, + val taskX: Int = Int.MIN_VALUE, + val taskY: Int = Int.MIN_VALUE, + ) + + /** + * Enum EnterReason mapped to the EnterReason definition in + * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto + */ + enum class EnterReason(val reason: Int) { + UNKNOWN_ENTER( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER + ), + OVERVIEW( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__OVERVIEW + ), + APP_HANDLE_DRAG( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_HANDLE_DRAG + ), + APP_HANDLE_MENU_BUTTON( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_HANDLE_MENU_BUTTON + ), + APP_FREEFORM_INTENT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_FREEFORM_INTENT + ), + KEYBOARD_SHORTCUT_ENTER( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER + ), + SCREEN_ON( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON + ); + } + + /** + * Enum ExitReason mapped to the ExitReason definition in + * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto + */ + enum class ExitReason(val reason: Int) { + UNKNOWN_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT + ), + DRAG_TO_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT + ), + APP_HANDLE_MENU_BUTTON_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__APP_HANDLE_MENU_BUTTON_EXIT + ), + KEYBOARD_SHORTCUT_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__KEYBOARD_SHORTCUT_EXIT + ), + RETURN_HOME_OR_OVERVIEW( + FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME + ), + TASK_FINISHED( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED + ), + SCREEN_OFF( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF + ) + } + + private const val DESKTOP_MODE_ATOM_ID = FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED + private const val DESKTOP_MODE_TASK_UPDATE_ATOM_ID = + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index fa14b4c64fe0..2933cf48614a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -3396,6 +3396,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final int stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(stageType, wct); + clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow", diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index 8c4711603904..32c070305e05 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -24,7 +24,10 @@ package { android_test { name: "WMShellUnitTests", - + defaults: [ + // For ExtendedMockito dependencies. + "modules-utils-testable-device-config-defaults", + ], srcs: [ "**/*.java", "**/*.kt", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt new file mode 100644 index 000000000000..4548fcb06c55 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.internal.util.FrameworkStatsLog +import com.android.modules.utils.testing.ExtendedMockitoRule +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.kotlin.eq + +/** + * Tests for [DesktopModeEventLogger]. + */ +class DesktopModeEventLoggerTest { + + private val desktopModeEventLogger = DesktopModeEventLogger() + + @JvmField + @Rule + val extendedMockitoRule = ExtendedMockitoRule.Builder(this) + .mockStatic(FrameworkStatsLog::class.java).build()!! + + @Test + fun logSessionEnter_enterReason() = runBlocking { + desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER) + + ExtendedMockito.verify { + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), + /* event */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER), + /* enter_reason */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER), + /* exit_reason */ + eq(0), + /* sessionId */ + eq(SESSION_ID) + ) + } + } + + @Test + fun logSessionExit_exitReason() = runBlocking { + desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT) + + ExtendedMockito.verify { + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), + /* event */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__EXIT), + /* enter_reason */ + eq(0), + /* exit_reason */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT), + /* sessionId */ + eq(SESSION_ID) + ) + } + } + + @Test + fun logTaskAdded_taskUpdate() = runBlocking { + desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE) + + ExtendedMockito.verify { + FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + /* task_event */ + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED), + /* instance_id */ + eq(TASK_UPDATE.instanceId), + /* uid */ + eq(TASK_UPDATE.uid), + /* task_height */ + eq(TASK_UPDATE.taskHeight), + /* task_width */ + eq(TASK_UPDATE.taskWidth), + /* task_x */ + eq(TASK_UPDATE.taskX), + /* task_y */ + eq(TASK_UPDATE.taskY), + /* session_id */ + eq(SESSION_ID)) + } + } + + @Test + fun logTaskRemoved_taskUpdate() = runBlocking { + desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE) + + ExtendedMockito.verify { + FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + /* task_event */ + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED), + /* instance_id */ + eq(TASK_UPDATE.instanceId), + /* uid */ + eq(TASK_UPDATE.uid), + /* task_height */ + eq(TASK_UPDATE.taskHeight), + /* task_width */ + eq(TASK_UPDATE.taskWidth), + /* task_x */ + eq(TASK_UPDATE.taskX), + /* task_y */ + eq(TASK_UPDATE.taskY), + /* session_id */ + eq(SESSION_ID)) + } + } + + @Test + fun logTaskInfoChanged_taskUpdate() = runBlocking { + desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE) + + ExtendedMockito.verify { + FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + /* task_event */ + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + /* instance_id */ + eq(TASK_UPDATE.instanceId), + /* uid */ + eq(TASK_UPDATE.uid), + /* task_height */ + eq(TASK_UPDATE.taskHeight), + /* task_width */ + eq(TASK_UPDATE.taskWidth), + /* task_x */ + eq(TASK_UPDATE.taskX), + /* task_y */ + eq(TASK_UPDATE.taskY), + /* session_id */ + eq(SESSION_ID)) + } + } + + companion object { + private const val SESSION_ID = 1 + private const val TASK_ID = 1 + private const val TASK_UID = 1 + private const val TASK_X = 0 + private const val TASK_Y = 0 + private const val TASK_HEIGHT = 100 + private const val TASK_WIDTH = 100 + + private val TASK_UPDATE = TaskUpdate( + TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y + ) + } +}
\ No newline at end of file diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index d21f07efe36a..b12486e32bd4 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -26,6 +26,7 @@ X(ClipPath) X(ClipRect) X(ClipRRect) X(ClipRegion) +X(ClipShader) X(ResetClip) X(DrawPaint) X(DrawBehind) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 3b694c5d399b..54aef55f8b90 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -207,6 +207,13 @@ struct ClipRegion final : Op { SkClipOp op; void draw(SkCanvas* c, const SkMatrix&) const { c->clipRegion(region, op); } }; +struct ClipShader final : Op { + static const auto kType = Type::ClipShader; + ClipShader(const sk_sp<SkShader>& shader, SkClipOp op) : shader(shader), op(op) {} + sk_sp<SkShader> shader; + SkClipOp op; + void draw(SkCanvas* c, const SkMatrix&) const { c->clipShader(shader, op); } +}; struct ResetClip final : Op { static const auto kType = Type::ResetClip; ResetClip() {} @@ -822,6 +829,9 @@ void DisplayListData::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) { void DisplayListData::clipRegion(const SkRegion& region, SkClipOp op) { this->push<ClipRegion>(0, region, op); } +void DisplayListData::clipShader(const sk_sp<SkShader>& shader, SkClipOp op) { + this->push<ClipShader>(0, shader, op); +} void DisplayListData::resetClip() { this->push<ResetClip>(0); } @@ -1134,6 +1144,11 @@ void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) { fDL->clipRegion(region, op); this->INHERITED::onClipRegion(region, op); } +void RecordingCanvas::onClipShader(sk_sp<SkShader> shader, SkClipOp op) { + setClipMayBeComplex(); + fDL->clipShader(shader, op); + this->INHERITED::onClipShader(shader, op); +} void RecordingCanvas::onResetClip() { // This is part of "replace op" emulation, but rely on the following intersection // clip to potentially mark the clip as complex. If we are already complex, we do diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index afadbfda7471..965264f31119 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -140,6 +140,7 @@ private: void translateZ(SkScalar); void clipPath(const SkPath&, SkClipOp, bool aa); + void clipShader(const sk_sp<SkShader>& shader, SkClipOp); void clipRect(const SkRect&, SkClipOp, bool aa); void clipRRect(const SkRRect&, SkClipOp, bool aa); void clipRegion(const SkRegion&, SkClipOp); @@ -216,6 +217,7 @@ public: void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override; void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override; void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override; + void onClipShader(sk_sp<SkShader>, SkClipOp) override; void onClipRegion(const SkRegion&, SkClipOp) override; void onResetClip() override; diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java index d1453975f801..ed543e67fdf1 100644 --- a/media/java/android/media/tv/ITvInputSessionWrapper.java +++ b/media/java/android/media/tv/ITvInputSessionWrapper.java @@ -519,7 +519,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand @Override public void stopPlayback(int mode) { - mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_STOP_PLAYBACK, mode)); + mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_STOP_PLAYBACK, mode)); } @Override diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts index 463e9be391b6..335725cdad5e 100644 --- a/packages/SettingsLib/Spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/build.gradle.kts @@ -29,7 +29,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath allprojects { extra["androidTop"] = androidTop - extra["jetpackComposeVersion"] = "1.7.0-alpha02" + extra["jetpackComposeVersion"] = "1.7.0-alpha03" } subprojects { diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml index fe378c27523c..609a82e79827 100644 --- a/packages/SettingsLib/Spa/gradle/libs.versions.toml +++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml @@ -16,7 +16,7 @@ [versions] agp = "8.2.2" -compose-compiler = "1.5.9" +compose-compiler = "1.5.10" dexmaker-mockito = "2.28.3" jvm = "17" kotlin = "1.9.22" diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png Binary files differindex a038779b0e79..e1f5c742dab7 100644 --- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png +++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png Binary files differindex 03db688f6799..928e9268292b 100644 --- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png +++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png Binary files differindex 1345c379cfb0..05213688b0a8 100644 --- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png +++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts index 2259bd74d56e..a193a2f9970d 100644 --- a/packages/SettingsLib/Spa/spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/spa/build.gradle.kts @@ -57,7 +57,7 @@ dependencies { api("androidx.slice:slice-builders:1.1.0-alpha02") api("androidx.slice:slice-core:1.1.0-alpha02") api("androidx.slice:slice-view:1.1.0-alpha02") - api("androidx.compose.material3:material3:1.2.0") + api("androidx.compose.material3:material3:1.3.0-alpha01") api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion") api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion") api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion") diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt new file mode 100644 index 000000000000..8300ce855988 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/RadioPreferences.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.widget.preference + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.material3.RadioButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.widget.ui.CategoryTitle +import com.android.settingslib.spa.widget.ui.SettingsListItem + +@Composable +fun RadioPreferences(model: ListPreferenceModel) { + CategoryTitle(title = model.title) + Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight)) + Column(modifier = Modifier.selectableGroup()) { + for (option in model.options) { + Radio2(option, model.selectedId.intValue, model.enabled()) { + model.onIdSelected(it) + } + } + } +} + +@Composable +fun Radio2( + option: ListPreferenceOption, + selectedId: Int, + enabled: Boolean, + onIdSelected: (id: Int) -> Unit, +) { + val selected = option.id == selectedId + Row( + modifier = Modifier + .fillMaxWidth() + .selectable( + selected = selected, + enabled = enabled, + onClick = { onIdSelected(option.id) }, + role = Role.RadioButton, + ) + .padding(SettingsDimension.dialogItemPadding), + verticalAlignment = Alignment.CenterVertically, + ) { + RadioButton(selected = selected, onClick = null, enabled = enabled) + Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight)) + SettingsListItem(text = option.text, enabled = enabled) + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt index 4f6196648634..56534f41c3df 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt @@ -180,7 +180,7 @@ private class TopAppBarColors( * @param colorTransitionFraction a `0.0` to `1.0` value that represents a color transition * percentage */ - @Composable + @Stable fun containerColor(colorTransitionFraction: Float): Color { return lerp( containerColor, @@ -519,7 +519,7 @@ private fun TopAppBarLayout( 0 } - val layoutHeight = if (heightPx.isNaN()) 0 else heightPx.roundToInt() + val layoutHeight = if (heightPx > 0) heightPx.roundToInt() else 0 layout(constraints.maxWidth, layoutHeight) { // Navigation icon diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt index b4a6a0d00720..a59b95a60879 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt @@ -66,6 +66,19 @@ fun SettingsDialogItem(text: String, enabled: Boolean = true) { } @Composable +fun SettingsListItem(text: String, enabled: Boolean = true) { + Text( + text = text, + modifier = Modifier + .alphaForEnabled(enabled) + .padding(vertical = SettingsDimension.paddingTiny), + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.titleMedium, + overflow = TextOverflow.Ellipsis, + ) +} + +@Composable fun SettingsBody( body: String, maxLines: Int = Int.MAX_VALUE, diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt new file mode 100644 index 000000000000..2f98b02b8dd5 --- /dev/null +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/RadioPreferencesTest.kt @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.widget.preference + +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsNotEnabled +import androidx.compose.ui.test.assertIsNotSelected +import androidx.compose.ui.test.assertIsSelectable +import androidx.compose.ui.test.assertIsSelected +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class RadioPreferencesTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun title_displayed() { + composeTestRule.setContent { + RadioPreferences(remember { + object : ListPreferenceModel { + override val title = TITLE + override val options = emptyList<ListPreferenceOption>() + override val selectedId = mutableIntStateOf(0) + override val onIdSelected: (Int) -> Unit = {} + } + }) + } + composeTestRule.onNodeWithText(TITLE).assertIsDisplayed() + } + + @Test + fun item_displayed() { + val selectedId = mutableIntStateOf(1) + composeTestRule.setContent { + RadioPreferences(remember { + object : ListPreferenceModel { + override val title = TITLE + override val options = listOf( + ListPreferenceOption(id = 1, text = "A"), + ListPreferenceOption(id = 2, text = "B"), + ) + override val selectedId = selectedId + override val onIdSelected = { id: Int -> selectedId.intValue = id } + } + }) + } + composeTestRule.onNodeWithText("A").assertIsDisplayed() + composeTestRule.onNodeWithText("B").assertIsDisplayed() + } + + @Test + fun item_selectable() { + val selectedId = mutableIntStateOf(1) + val enabledState = mutableStateOf(true) + composeTestRule.setContent { + RadioPreferences(remember { + object : ListPreferenceModel { + override val title = TITLE + override val enabled = { enabledState.value } + override val options = listOf( + ListPreferenceOption(id = 1, text = "A"), + ListPreferenceOption(id = 2, text = "B"), + ) + override val selectedId = selectedId + override val onIdSelected = { id: Int -> selectedId.intValue = id } + } + }) + } + composeTestRule.onNodeWithText("A").assertIsSelectable() + composeTestRule.onNodeWithText("B").assertIsSelectable() + } + + @Test + fun item_single_selected() { + val selectedId = mutableIntStateOf(1) + val enabledState = mutableStateOf(true) + composeTestRule.setContent { + RadioPreferences(remember { + object : ListPreferenceModel { + override val title = TITLE + override val enabled = { enabledState.value } + override val options = listOf( + ListPreferenceOption(id = 1, text = "A"), + ListPreferenceOption(id = 2, text = "B"), + ) + override val selectedId = selectedId + override val onIdSelected = { id: Int -> selectedId.intValue = id } + } + }) + } + composeTestRule.onNodeWithText("B").assertIsSelectable() + composeTestRule.onNodeWithText("B").performClick() + composeTestRule.onNodeWithText("B").assertIsSelected() + composeTestRule.onNodeWithText("A").assertIsNotSelected() + composeTestRule.onNodeWithText("A").performClick() + composeTestRule.onNodeWithText("A").assertIsSelected() + composeTestRule.onNodeWithText("B").assertIsNotSelected() + } + + @Test + fun select_itemDisabled() { + val selectedId = mutableIntStateOf(1) + val enabledState = mutableStateOf(true) + composeTestRule.setContent { + RadioPreferences(remember { + object : ListPreferenceModel { + override val title = TITLE + override val enabled = { enabledState.value } + override val options = listOf( + ListPreferenceOption(id = 1, text = "A"), + ListPreferenceOption(id = 2, text = "B"), + ) + override val selectedId = selectedId + override val onIdSelected = { id: Int -> selectedId.intValue = id } + } + }) + } + enabledState.value = false + composeTestRule.onNodeWithText("A").assertIsDisplayed().assertIsNotEnabled() + composeTestRule.onNodeWithText("B").assertIsDisplayed().assertIsNotEnabled() + } + + private companion object { + const val TITLE = "Title" + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 9588e502f28e..a4bc235aaa7a 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1085,7 +1085,7 @@ <!-- [CHAR_LIMIT=NONE] Label for charging on hold on main page of settings --> <string name="power_charging_on_hold_settings_home_page"><xliff:g id="level">%1$s</xliff:g> - Charging on hold to protect battery</string> <!-- [CHAR_LIMIT=NONE] Label for incompatible charging accessory on main page of settings --> - <string name="power_incompatible_charging_settings_home_page"><xliff:g id="level">%1$s</xliff:g> - Checking charging accessory</string> + <string name="power_incompatible_charging_settings_home_page"><xliff:g id="level">%1$s</xliff:g> - Check charging accessory</string> <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging --> <string name="power_remaining_duration_only">About <xliff:g id="time_remaining">%1$s</xliff:g> left</string> <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration --> diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index a69a2a64dccb..f5c4843e1324 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -546,3 +546,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "notify_power_manager_user_activity_background" + namespace: "systemui" + description: "Decide whether to notify the user activity to power manager in the background thread." + bug: "325203885" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp index c1125f0b9e92..99b7c36d6fb9 100644 --- a/packages/SystemUI/animation/Android.bp +++ b/packages/SystemUI/animation/Android.bp @@ -45,6 +45,7 @@ android_library { "androidx.core_core-ktx", "androidx.annotation_annotation", "SystemUIShaderLib", + "WindowManager-Shell-shared", "animationlib", ], diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java index a78080fdabf2..e20425d4b98c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.shared.system; +package com.android.systemui.animation; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.view.WindowManager.TRANSIT_CLOSE; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationTargetCompat.java index e4d924323bbd..e251af44727e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationTargetCompat.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.shared.system; +package com.android.systemui.animation; import android.util.ArrayMap; import android.view.RemoteAnimationTarget; diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt index a197a4b0fd55..ddc9252a5a4a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt @@ -36,6 +36,8 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonDefaults @@ -66,9 +68,9 @@ fun ColumnVolumeSliders( modifier: Modifier = Modifier, ) { require(viewModels.isNotEmpty()) - var isExpanded: Boolean by remember { mutableStateOf(false) } + var isExpanded: Boolean by remember(isExpandable) { mutableStateOf(!isExpandable) } val transition = updateTransition(isExpanded, label = "CollapsableSliders") - Column(modifier = modifier) { + Column(modifier = modifier.verticalScroll(rememberScrollState())) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp), diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt index 5925b1482e77..0d94bb06c06f 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt @@ -41,7 +41,7 @@ fun VolumeSlider( modifier: Modifier = Modifier, sliderColors: PlatformSliderColors, ) { - var value by remember { mutableFloatStateOf(state.value) } + var value by remember(state.value) { mutableFloatStateOf(state.value) } PlatformSlider( modifier = modifier, value = value, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt index 6213dc5f63c9..1ca18deeaac2 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt @@ -26,6 +26,7 @@ import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.Sl import com.android.systemui.volume.panel.component.volume.ui.viewmodel.AudioVolumeComponentViewModel import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope +import com.android.systemui.volume.panel.ui.composable.isPortrait import javax.inject.Inject class VolumeSlidersComponent @@ -50,7 +51,7 @@ constructor( ColumnVolumeSliders( viewModels = sliderViewModels, sliderColors = PlatformSliderDefaults.defaultPlatformSliderColors(), - isExpandable = true, + isExpandable = isPortrait, modifier = modifier.fillMaxWidth(), ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt index 0a651c898aba..a838a99524a3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt @@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -56,7 +55,7 @@ fun VolumePanelComposeScope.HorizontalVolumePanelContent( } } Row( - modifier = Modifier.fillMaxWidth().wrapContentHeight(), + modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(spacing), ) { for (component in layout.footerComponents) { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt index 8a1e6a8b74f4..8a9ebc918be6 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt @@ -16,7 +16,6 @@ package com.android.systemui.volume.panel.ui.composable -import android.content.res.Configuration import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement @@ -111,15 +110,24 @@ private fun VolumePanelComposeScope.Components( layout: ComponentsLayout, modifier: Modifier = Modifier ) { - var columnModifier = modifier.widthIn(max = 800.dp) - if (!isLargeScreen && orientation != Configuration.ORIENTATION_PORTRAIT) { - columnModifier = columnModifier.heightIn(max = 332.dp) - } - Column(modifier = columnModifier, verticalArrangement = Arrangement.spacedBy(padding)) { - if (orientation == Configuration.ORIENTATION_PORTRAIT || isLargeScreen) { - VerticalVolumePanelContent(layout) + val arrangement = + if (isLargeScreen) { + Arrangement.spacedBy(20.dp) + } else { + if (isPortrait) Arrangement.spacedBy(padding) else Arrangement.spacedBy(4.dp) + } + Column( + modifier = modifier.widthIn(max = 800.dp), + verticalArrangement = arrangement, + ) { + val contentModifier = Modifier + if (isPortrait || isLargeScreen) { + VerticalVolumePanelContent(modifier = contentModifier, layout = layout) } else { - HorizontalVolumePanelContent(layout) + HorizontalVolumePanelContent( + modifier = contentModifier.heightIn(max = 212.dp), + layout = layout + ) } BottomBar(layout = layout, modifier = Modifier) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index 2e781e69bb4a..c7186da6b961 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -39,7 +39,6 @@ import androidx.compose.ui.layout.Placeable import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.node.ModifierNodeElement import androidx.compose.ui.platform.testTag -import androidx.compose.ui.semantics.testTag import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.round @@ -204,6 +203,17 @@ internal class ElementNode( measurable: Measurable, constraints: Constraints, ): MeasureResult { + val overscrollScene = layoutImpl.state.currentOverscrollSpec?.scene + if (overscrollScene != null && overscrollScene != scene.key) { + // There is an overscroll in progress on another scene + // By measuring composable elements, Compose can cache relevant information. + // This reduces the need for re-measure when users return from an overscroll animation. + val placeable = measurable.measure(constraints) + return layout(placeable.width, placeable.height) { + // We don't want to draw it, no need to place the element. + } + } + val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints) return layout(placeable.width, placeable.height) { place(layoutImpl, scene, element, sceneState, placeable, placementScope = this) @@ -253,11 +263,13 @@ private fun shouldDrawElement( ): Boolean { val transition = layoutImpl.state.currentTransition - // Always draw the element if there is no ongoing transition or if the element is not shared. + // Always draw the element if there is no ongoing transition or if the element is not shared or + // if the current scene is the one that is currently over scrolling with [OverscrollSpec]. if ( transition == null || transition.fromScene !in element.sceneStates || - transition.toScene !in element.sceneStates + transition.toScene !in element.sceneStates || + layoutImpl.state.currentOverscrollSpec?.scene == scene.key ) { return true } @@ -286,12 +298,14 @@ internal fun shouldDrawOrComposeSharedElement( val fromScene = transition.fromScene val toScene = transition.toScene - return scenePicker.sceneDuringTransition( - element = element, - transition = transition, - fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex, - toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex, - ) == scene + val chosenByPicker = + scenePicker.sceneDuringTransition( + element = element, + transition = transition, + fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex, + toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex, + ) == scene + return chosenByPicker || layoutImpl.state.currentOverscrollSpec?.scene == scene } private fun isSharedElementEnabled( @@ -549,6 +563,40 @@ private inline fun <T> computeValue( return idleValue } + if (transition is TransitionState.HasOverscrollProperties) { + val overscroll = layoutImpl.state.currentOverscrollSpec + if (overscroll?.scene == scene.key) { + val elementSpec = overscroll.transformationSpec.transformations(element.key, scene.key) + val propertySpec = transformation(elementSpec) ?: return currentValue() + val overscrollState = checkNotNull(if (scene.key == toScene) toState else fromState) + val targetValue = + propertySpec.transform( + layoutImpl, + scene, + element, + overscrollState, + transition, + idleValue, + ) + + // Make sure we don't read progress if values are the same and we don't need to + // interpolate, so we don't invalidate the phase where this is read. + if (targetValue == idleValue) { + return targetValue + } + + // TODO(b/290184746): Make sure that we don't overflow transformations associated to a + // range. + val directionSign = if (transition.isUpOrLeft) -1 else 1 + val overscrollProgress = transition.progress.let { if (it > 1f) it - 1f else it } + val progress = directionSign * overscrollProgress + val rangeProgress = propertySpec.range?.progress(progress) ?: progress + + // Interpolate between the value at rest and the over scrolled value. + return lerp(idleValue, targetValue, rangeProgress) + } + } + // The element is shared: interpolate between the value in fromScene and the value in toScene. // TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared // elements follow the finger direction. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index c40856061152..187d82a9e626 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -497,9 +497,11 @@ private class SwipeTransition( val _fromScene: Scene, val _toScene: Scene, private val userActionDistanceScope: UserActionDistanceScope, - private val orientation: Orientation, - private val isUpOrLeft: Boolean, -) : TransitionState.Transition(_fromScene.key, _toScene.key) { + override val orientation: Orientation, + override val isUpOrLeft: Boolean, +) : + TransitionState.Transition(_fromScene.key, _toScene.key), + TransitionState.HasOverscrollProperties { var _currentScene by mutableStateOf(_fromScene) override val currentScene: SceneKey get() = _currentScene.key @@ -789,14 +791,21 @@ internal class SceneNestedScrollHandler( ) fun hasNextScene(amount: Float): Boolean { - val fromScene = layoutImpl.scene(layoutState.transitionState.currentScene) + val transitionState = layoutState.transitionState + val scene = transitionState.currentScene + val fromScene = layoutImpl.scene(scene) val nextScene = when { amount < 0f -> fromScene.userActions[actionUpOrLeft] amount > 0f -> fromScene.userActions[actionDownOrRight] else -> null } - return nextScene != null + if (nextScene != null) return true + + if (transitionState !is TransitionState.Idle) return false + + val overscrollSpec = layoutImpl.state.transitions.overscrollSpec(scene, orientation) + return overscrollSpec != null } val source = this diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index 039a5b0c9523..3093d477a24c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -25,8 +25,13 @@ import androidx.compose.runtime.key import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ApproachLayoutModifierNode +import androidx.compose.ui.layout.ApproachMeasureScope import androidx.compose.ui.layout.LookaheadScope -import androidx.compose.ui.layout.intermediateLayout +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.IntSize import androidx.compose.ui.util.fastForEach @@ -181,7 +186,6 @@ internal class SceneTransitionLayoutImpl( } @Composable - @OptIn(ExperimentalComposeUiApi::class) internal fun Content(modifier: Modifier) { Box( modifier @@ -190,37 +194,7 @@ internal class SceneTransitionLayoutImpl( // swipes. .swipeToScene(horizontalGestureHandler) .swipeToScene(verticalGestureHandler) - // Animate the size of this layout. - .intermediateLayout { measurable, constraints -> - // Measure content normally. - val placeable = measurable.measure(constraints) - - val width: Int - val height: Int - val transition = state.currentTransition - if (transition == null) { - width = placeable.width - height = placeable.height - } else { - // Interpolate the size. - val fromSize = scene(transition.fromScene).targetSize - val toSize = scene(transition.toScene).targetSize - - // Optimization: make sure we don't read state.progress if fromSize == - // toSize to avoid running this code every frame when the layout size does - // not change. - if (fromSize == toSize) { - width = fromSize.width - height = fromSize.height - } else { - val size = lerp(fromSize, toSize, transition.progress) - width = size.width.coerceAtLeast(0) - height = size.height.coerceAtLeast(0) - } - } - - layout(width, height) { placeable.place(0, 0) } - } + .then(LayoutElement(layoutImpl = this)) ) { LookaheadScope { val scenesToCompose = @@ -263,3 +237,54 @@ internal class SceneTransitionLayoutImpl( scenes.values.forEach { it.targetSize = size } } } + +private data class LayoutElement(private val layoutImpl: SceneTransitionLayoutImpl) : + ModifierNodeElement<LayoutNode>() { + override fun create(): LayoutNode = LayoutNode(layoutImpl) + + override fun update(node: LayoutNode) { + node.layoutImpl = layoutImpl + } +} + +private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) : + Modifier.Node(), ApproachLayoutModifierNode { + override fun isMeasurementApproachComplete(lookaheadSize: IntSize): Boolean { + return layoutImpl.state.currentTransition == null + } + + @ExperimentalComposeUiApi + override fun ApproachMeasureScope.approachMeasure( + measurable: Measurable, + constraints: Constraints, + ): MeasureResult { + // Measure content normally. + val placeable = measurable.measure(constraints) + + val width: Int + val height: Int + val transition = layoutImpl.state.currentTransition + if (transition == null) { + width = placeable.width + height = placeable.height + } else { + // Interpolate the size. + val fromSize = layoutImpl.scene(transition.fromScene).targetSize + val toSize = layoutImpl.scene(transition.toScene).targetSize + + // Optimization: make sure we don't read state.progress if fromSize == + // toSize to avoid running this code every frame when the layout size does + // not change. + if (fromSize == toSize) { + width = fromSize.width + height = fromSize.height + } else { + val size = lerp(fromSize, toSize, transition.progress) + width = size.width.coerceAtLeast(0) + height = size.height.coerceAtLeast(0) + } + } + + return layout(width, height) { placeable.place(0, 0) } + } +} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 662f33f3e88b..0fa19bb33818 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -16,6 +16,7 @@ package com.android.compose.animation.scene +import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect @@ -221,6 +222,23 @@ sealed interface TransitionState { isTransitioning(from = other, to = scene) } } + + interface HasOverscrollProperties { + /** + * The position of the [TransitionState.Transition.toScene]. + * + * Used to understand the direction of the overscroll. + */ + val isUpOrLeft: Boolean + + /** + * The relative orientation between [TransitionState.Transition.fromScene] and + * [TransitionState.Transition.toScene]. + * + * Used to understand the orientation of the overscroll. + */ + val orientation: Orientation + } } internal abstract class BaseSceneTransitionLayoutState( @@ -237,6 +255,25 @@ internal abstract class BaseSceneTransitionLayoutState( */ internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty + private var fromOverscrollSpec: OverscrollSpecImpl? = null + private var toOverscrollSpec: OverscrollSpecImpl? = null + + /** + * @return the overscroll [OverscrollSpecImpl] if it is defined for the current + * [transitionState] and we are currently over scrolling. + */ + internal val currentOverscrollSpec: OverscrollSpecImpl? + get() { + val transition = currentTransition ?: return null + if (transition !is TransitionState.HasOverscrollProperties) return null + val progress = transition.progress + return when { + progress < 0f -> fromOverscrollSpec + progress > 1f -> toOverscrollSpec + else -> null + } + } + private val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>() /** Whether we can transition to the given [scene]. */ @@ -266,10 +303,13 @@ internal abstract class BaseSceneTransitionLayoutState( transitionKey: TransitionKey?, ) { // Compute the [TransformationSpec] when the transition starts. + val fromScene = transition.fromScene + val toScene = transition.toScene + val orientation = (transition as? TransitionState.HasOverscrollProperties)?.orientation transformationSpec = - transitions - .transitionSpec(transition.fromScene, transition.toScene, key = transitionKey) - .transformationSpec() + transitions.transitionSpec(fromScene, toScene, key = transitionKey).transformationSpec() + fromOverscrollSpec = orientation?.let { transitions.overscrollSpec(fromScene, it) } + toOverscrollSpec = orientation?.let { transitions.overscrollSpec(toScene, it) } cancelActiveTransitionLinks() setupTransitionLinks(transition) transitionState = transition diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt index 8ee23b6ffe8e..2dd41cd329a2 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt @@ -21,6 +21,7 @@ import androidx.compose.animation.core.Spring import androidx.compose.animation.core.SpringSpec import androidx.compose.animation.core.snap import androidx.compose.animation.core.spring +import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.util.fastForEach @@ -41,18 +42,22 @@ class SceneTransitions internal constructor( internal val defaultSwipeSpec: SpringSpec<Float>, internal val transitionSpecs: List<TransitionSpecImpl>, + internal val overscrollSpecs: List<OverscrollSpecImpl>, ) { - private val cache = + private val transitionCache = mutableMapOf< SceneKey, MutableMap<SceneKey, MutableMap<TransitionKey?, TransitionSpecImpl>> >() + private val overscrollCache = + mutableMapOf<SceneKey, MutableMap<Orientation, OverscrollSpecImpl?>>() + internal fun transitionSpec( from: SceneKey, to: SceneKey, key: TransitionKey?, ): TransitionSpecImpl { - return cache + return transitionCache .getOrPut(from) { mutableMapOf() } .getOrPut(to) { mutableMapOf() } .getOrPut(key) { findSpec(from, to, key) } @@ -105,6 +110,28 @@ internal constructor( private fun defaultTransition(from: SceneKey, to: SceneKey) = TransitionSpecImpl(key = null, from, to, TransformationSpec.EmptyProvider) + internal fun overscrollSpec(scene: SceneKey, orientation: Orientation): OverscrollSpecImpl? = + overscrollCache + .getOrPut(scene) { mutableMapOf() } + .getOrPut(orientation) { overscroll(scene, orientation) { it.scene == scene } } + + private fun overscroll( + scene: SceneKey, + orientation: Orientation, + filter: (OverscrollSpecImpl) -> Boolean, + ): OverscrollSpecImpl? { + var match: OverscrollSpecImpl? = null + overscrollSpecs.fastForEach { spec -> + if (spec.orientation == orientation && filter(spec)) { + if (match != null) { + error("Found multiple transition specs for transition $scene") + } + match = spec + } + } + return match + } + companion object { internal val DefaultSwipeSpec = spring( @@ -112,7 +139,12 @@ internal constructor( visibilityThreshold = OffsetVisibilityThreshold, ) - val Empty = SceneTransitions(DefaultSwipeSpec, transitionSpecs = emptyList()) + val Empty = + SceneTransitions( + defaultSwipeSpec = DefaultSwipeSpec, + transitionSpecs = emptyList(), + overscrollSpecs = emptyList(), + ) } } @@ -139,7 +171,7 @@ interface TransitionSpec { */ fun reversed(): TransitionSpec - /* + /** * The [TransformationSpec] associated to this [TransitionSpec]. * * Note that this is called once every a transition associated to this [TransitionSpec] is @@ -212,6 +244,24 @@ internal class TransitionSpecImpl( override fun transformationSpec(): TransformationSpecImpl = this.transformationSpec.invoke() } +/** The definition of the overscroll behavior of the [scene]. */ +interface OverscrollSpec { + /** The scene we are over scrolling. */ + val scene: SceneKey + + /** The orientation of this [OverscrollSpec]. */ + val orientation: Orientation + + /** The [TransformationSpec] associated to this [OverscrollSpec]. */ + val transformationSpec: TransformationSpec +} + +internal class OverscrollSpecImpl( + override val scene: SceneKey, + override val orientation: Orientation, + override val transformationSpec: TransformationSpecImpl, +) : OverscrollSpec + /** * An implementation of [TransformationSpec] that allows the quick retrieval of an element * [ElementTransformations]. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt index 8a09b00a63ae..bc52a28279dc 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt @@ -18,6 +18,7 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.AnimationSpec import androidx.compose.animation.core.SpringSpec +import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -72,24 +73,23 @@ interface SceneTransitionsBuilder { key: TransitionKey? = null, builder: TransitionBuilder.() -> Unit = {}, ): TransitionSpec -} - -@TransitionDsl -interface TransitionBuilder : PropertyTransformationBuilder { - /** - * The [AnimationSpec] used to animate the associated transition progress from `0` to `1` when - * the transition is triggered (i.e. it is not gesture-based). - */ - var spec: AnimationSpec<Float> /** - * The [SpringSpec] used to animate the associated transition progress when the transition was - * started by a swipe and is now animating back to a scene because the user lifted their finger. + * Define the animation to be played when the [scene] is overscrolled in the given + * [orientation]. * - * If `null`, then the [SceneTransitionsBuilder.defaultSwipeSpec] will be used. + * The overscroll animation always starts from a progress of 0f, and reaches 1f when moving the + * [distance] down/right, -1f when moving in the opposite direction. */ - var swipeSpec: SpringSpec<Float>? + fun overscroll( + scene: SceneKey, + orientation: Orientation, + builder: OverscrollBuilder.() -> Unit = {}, + ): OverscrollSpec +} +@TransitionDsl +interface OverscrollBuilder : PropertyTransformationBuilder { /** * The distance it takes for this transition to animate from 0% to 100% when it is driven by a * [UserAction]. @@ -117,6 +117,23 @@ interface TransitionBuilder : PropertyTransformationBuilder { end: Float? = null, builder: PropertyTransformationBuilder.() -> Unit, ) +} + +@TransitionDsl +interface TransitionBuilder : OverscrollBuilder, PropertyTransformationBuilder { + /** + * The [AnimationSpec] used to animate the associated transition progress from `0` to `1` when + * the transition is triggered (i.e. it is not gesture-based). + */ + var spec: AnimationSpec<Float> + + /** + * The [SpringSpec] used to animate the associated transition progress when the transition was + * started by a swipe and is now animating back to a scene because the user lifted their finger. + * + * If `null`, then the [SceneTransitionsBuilder.defaultSwipeSpec] will be used. + */ + var swipeSpec: SpringSpec<Float>? /** * Define a timestamp-based range for the transformations inside [builder]. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt index 78289997885f..65e8ea5cc341 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt @@ -21,7 +21,9 @@ import androidx.compose.animation.core.DurationBasedAnimationSpec import androidx.compose.animation.core.Spring import androidx.compose.animation.core.SpringSpec import androidx.compose.animation.core.VectorConverter +import androidx.compose.animation.core.snap import androidx.compose.animation.core.spring +import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.Dp import com.android.compose.animation.scene.transformation.AnchoredSize @@ -41,13 +43,18 @@ internal fun transitionsImpl( builder: SceneTransitionsBuilder.() -> Unit, ): SceneTransitions { val impl = SceneTransitionsBuilderImpl().apply(builder) - return SceneTransitions(impl.defaultSwipeSpec, impl.transitionSpecs) + return SceneTransitions( + impl.defaultSwipeSpec, + impl.transitionSpecs, + impl.transitionOverscrollSpecs + ) } private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { override var defaultSwipeSpec: SpringSpec<Float> = SceneTransitions.DefaultSwipeSpec val transitionSpecs = mutableListOf<TransitionSpecImpl>() + val transitionOverscrollSpecs = mutableListOf<OverscrollSpecImpl>() override fun to( to: SceneKey, @@ -66,6 +73,25 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { return transition(from = from, to = to, key = key, builder) } + override fun overscroll( + scene: SceneKey, + orientation: Orientation, + builder: OverscrollBuilder.() -> Unit + ): OverscrollSpec { + fun transformationSpec(): TransformationSpecImpl { + val impl = OverscrollBuilderImpl().apply(builder) + return TransformationSpecImpl( + progressSpec = snap(), + swipeSpec = null, + distance = impl.distance, + transformations = impl.transformations, + ) + } + val spec = OverscrollSpecImpl(scene, orientation, transformationSpec()) + transitionOverscrollSpecs.add(spec) + return spec + } + private fun transition( from: SceneKey?, to: SceneKey?, @@ -88,28 +114,11 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { } } -internal class TransitionBuilderImpl : TransitionBuilder { +internal open class OverscrollBuilderImpl : OverscrollBuilder { val transformations = mutableListOf<Transformation>() - override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow) - override var swipeSpec: SpringSpec<Float>? = null - override var distance: UserActionDistance? = null - private var range: TransformationRange? = null - private var reversed = false - private val durationMillis: Int by lazy { - val spec = spec - if (spec !is DurationBasedAnimationSpec) { - error("timestampRange {} can only be used with a DurationBasedAnimationSpec") - } - - spec.vectorize(Float.VectorConverter).durationMillis - } - - override fun reversed(builder: TransitionBuilder.() -> Unit) { - reversed = true - builder() - reversed = false - } + protected var reversed = false + override var distance: UserActionDistance? = null override fun fractionRange( start: Float?, @@ -121,28 +130,6 @@ internal class TransitionBuilderImpl : TransitionBuilder { range = null } - override fun sharedElement(matcher: ElementMatcher, enabled: Boolean) { - transformations.add(SharedElementTransformation(matcher, enabled)) - } - - override fun timestampRange( - startMillis: Int?, - endMillis: Int?, - builder: PropertyTransformationBuilder.() -> Unit - ) { - if (startMillis != null && (startMillis < 0 || startMillis > durationMillis)) { - error("invalid start value: startMillis=$startMillis durationMillis=$durationMillis") - } - - if (endMillis != null && (endMillis < 0 || endMillis > durationMillis)) { - error("invalid end value: endMillis=$startMillis durationMillis=$durationMillis") - } - - val start = startMillis?.let { it.toFloat() / durationMillis } - val end = endMillis?.let { it.toFloat() / durationMillis } - fractionRange(start, end, builder) - } - private fun transformation(transformation: PropertyTransformation<*>) { val transformation = if (range != null) { @@ -197,3 +184,45 @@ internal class TransitionBuilderImpl : TransitionBuilder { transformation(AnchoredSize(matcher, anchor, anchorWidth, anchorHeight)) } } + +internal class TransitionBuilderImpl : OverscrollBuilderImpl(), TransitionBuilder { + override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow) + override var swipeSpec: SpringSpec<Float>? = null + override var distance: UserActionDistance? = null + private val durationMillis: Int by lazy { + val spec = spec + if (spec !is DurationBasedAnimationSpec) { + error("timestampRange {} can only be used with a DurationBasedAnimationSpec") + } + + spec.vectorize(Float.VectorConverter).durationMillis + } + + override fun reversed(builder: TransitionBuilder.() -> Unit) { + reversed = true + builder() + reversed = false + } + + override fun sharedElement(matcher: ElementMatcher, enabled: Boolean) { + transformations.add(SharedElementTransformation(matcher, enabled)) + } + + override fun timestampRange( + startMillis: Int?, + endMillis: Int?, + builder: PropertyTransformationBuilder.() -> Unit + ) { + if (startMillis != null && (startMillis < 0 || startMillis > durationMillis)) { + error("invalid start value: startMillis=$startMillis durationMillis=$durationMillis") + } + + if (endMillis != null && (endMillis < 0 || endMillis > durationMillis)) { + error("invalid end value: endMillis=$startMillis durationMillis=$durationMillis") + } + + val start = startMillis?.let { it.toFloat() / durationMillis } + val end = endMillis?.let { it.toFloat() / durationMillis } + fractionRange(start, end, builder) + } +} diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index 33be1dc83dc8..059a10e23207 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -18,9 +18,13 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.tween import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.rememberScrollableState +import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size @@ -34,8 +38,14 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.layout.intermediateLayout +import androidx.compose.ui.platform.LocalViewConfiguration +import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -528,4 +538,157 @@ class ElementTest { after { assertThat(fooCompositions).isEqualTo(1) } } } + + @Test + fun elementTransitionDuringOverscroll() { + // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is + // detected as a drag event. + var touchSlop = 0f + val overscrollTranslateY = 10.dp + val layoutWidth = 200.dp + val layoutHeight = 400.dp + + val state = + MutableSceneTransitionLayoutState( + initialScene = TestScenes.SceneA, + transitions = + transitions { + overscroll(TestScenes.SceneB, Orientation.Vertical) { + translate(TestElements.Foo, y = overscrollTranslateY) + } + } + ) + as MutableSceneTransitionLayoutStateImpl + + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout( + state = state, + modifier = Modifier.size(layoutWidth, layoutHeight) + ) { + scene( + key = TestScenes.SceneA, + userActions = mapOf(Swipe.Down to TestScenes.SceneB) + ) { + Spacer(Modifier.fillMaxSize()) + } + scene(TestScenes.SceneB) { + Spacer(Modifier.element(TestElements.Foo).fillMaxSize()) + } + } + } + + assertThat(state.currentTransition).isNull() + assertThat(state.currentOverscrollSpec).isNull() + + // Swipe by half of verticalSwipeDistance. + rule.onRoot().performTouchInput { + val middleTop = Offset((layoutWidth / 2).toPx(), 0f) + down(middleTop) + // Scroll 50% + moveBy(Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), delayMillis = 1_000) + } + + val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true) + fooElement.assertTopPositionInRootIsEqualTo(0.dp) + val transition = state.currentTransition + assertThat(transition).isNotNull() + assertThat(transition!!.progress).isEqualTo(0.5f) + + rule.onRoot().performTouchInput { + // Scroll another 100% + moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000) + } + + // Scroll 150% (Scene B overscroll by 50%) + assertThat(transition.progress).isEqualTo(1.5f) + assertThat(state.currentOverscrollSpec).isNotNull() + fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f) + + rule.onRoot().performTouchInput { + // Scroll another 100% + moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000) + } + + // Scroll 250% (Scene B overscroll by 150%) + assertThat(transition.progress).isEqualTo(2.5f) + assertThat(state.currentOverscrollSpec).isNotNull() + fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f) + } + + @Test + fun elementTransitionDuringNestedScrollOverscroll() { + // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is + // detected as a drag event. + var touchSlop = 0f + val overscrollTranslateY = 10.dp + val layoutWidth = 200.dp + val layoutHeight = 400.dp + + val state = + MutableSceneTransitionLayoutState( + initialScene = TestScenes.SceneB, + transitions = + transitions { + overscroll(TestScenes.SceneB, Orientation.Vertical) { + translate(TestElements.Foo, y = overscrollTranslateY) + } + } + ) + as MutableSceneTransitionLayoutStateImpl + + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout( + state = state, + modifier = Modifier.size(layoutWidth, layoutHeight) + ) { + scene(TestScenes.SceneA) { Spacer(Modifier.fillMaxSize()) } + scene(TestScenes.SceneB, userActions = mapOf(Swipe.Up to TestScenes.SceneA)) { + Box( + Modifier + // Unconsumed scroll gesture will be intercepted by STL + .verticalNestedScrollToScene() + // A scrollable that does not consume the scroll gesture + .scrollable( + rememberScrollableState(consumeScrollDelta = { 0f }), + Orientation.Vertical + ) + .fillMaxSize() + ) { + Spacer(Modifier.element(TestElements.Foo).fillMaxSize()) + } + } + } + } + + assertThat(state.currentTransition).isNull() + assertThat(state.currentOverscrollSpec).isNull() + val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true) + fooElement.assertTopPositionInRootIsEqualTo(0.dp) + + // Swipe by half of verticalSwipeDistance. + rule.onRoot().performTouchInput { + val middleTop = Offset((layoutWidth / 2).toPx(), 0f) + down(middleTop) + // Scroll 50% + moveBy(Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), delayMillis = 1_000) + } + + val transition = state.currentTransition + assertThat(state.currentOverscrollSpec).isNotNull() + assertThat(transition).isNotNull() + assertThat(transition!!.progress).isEqualTo(-0.5f) + fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f) + + rule.onRoot().performTouchInput { + // Scroll another 100% + moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000) + } + + // Scroll 150% (Scene B overscroll by 50%) + assertThat(transition.progress).isEqualTo(-1.5f) + assertThat(state.currentOverscrollSpec).isNotNull() + fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index fe53d5b45420..d28ac6ad546e 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -52,7 +52,7 @@ class SceneGestureHandlerTest { private val testScope: MonotonicClockTestScope, ) { var canChangeScene: (SceneKey) -> Boolean = { true } - private val layoutState = + val layoutState = MutableSceneTransitionLayoutStateImpl( SceneA, EmptyTestTransitions, @@ -932,4 +932,28 @@ class SceneGestureHandlerTest { advanceUntilIdle() assertIdle(SceneB) } + + @Test + fun scrollFromIdleWithNoTargetScene_shouldUseOverscrollSpecIfAvailable() = runGestureTest { + layoutState.transitions = transitions { + overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) } + } + // Start at scene C. + navigateToSceneC() + + val scene = layoutState.transitionState.currentScene + // We should have overscroll spec for scene C + assertThat(layoutState.transitions.overscrollSpec(scene, Orientation.Vertical)).isNotNull() + assertThat(layoutState.currentOverscrollSpec).isNull() + + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) + nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f)) + + // We scrolled down, under scene C there is nothing, so we can use the overscroll spec + assertThat(layoutState.currentOverscrollSpec).isNotNull() + assertThat(layoutState.currentOverscrollSpec?.scene).isEqualTo(SceneC) + val transition = layoutState.currentTransition + assertThat(transition).isNotNull() + assertThat(transition!!.progress).isEqualTo(-0.1f) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index f81a7f2908e8..3cbcd73a0adb 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -16,6 +16,8 @@ package com.android.compose.animation.scene +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.animation.scene.TestScenes.SceneA @@ -389,4 +391,118 @@ class SceneTransitionLayoutStateTest { assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() assertThat(parentState.isTransitioning(SceneC, SceneD)).isFalse() } + + private fun startOverscrollableTransistionFromAtoB( + progress: () -> Float, + sceneTransitions: SceneTransitions, + ): MutableSceneTransitionLayoutStateImpl { + val state = + MutableSceneTransitionLayoutStateImpl( + SceneA, + sceneTransitions, + ) + state.startTransition( + object : + TransitionState.Transition(SceneA, SceneB), + TransitionState.HasOverscrollProperties { + override val currentScene: SceneKey = SceneA + override val progress: Float + get() = progress() + + override val isInitiatedByUserInput: Boolean = false + override val isUserInputOngoing: Boolean = false + override val isUpOrLeft: Boolean = false + override val orientation: Orientation = Orientation.Vertical + }, + transitionKey = null + ) + assertThat(state.isTransitioning()).isTrue() + return state + } + + @Test + fun overscrollDsl_definedForToScene() = runMonotonicClockTest { + val progress = mutableStateOf(0f) + val state = + startOverscrollableTransistionFromAtoB( + progress = { progress.value }, + sceneTransitions = + transitions { + overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) } + } + ) + assertThat(state.currentOverscrollSpec).isNull() + + // overscroll for SceneA is NOT defined + progress.value = -0.1f + assertThat(state.currentOverscrollSpec).isNull() + + // scroll from SceneA to SceneB + progress.value = 0.5f + assertThat(state.currentOverscrollSpec).isNull() + + progress.value = 1f + assertThat(state.currentOverscrollSpec).isNull() + + // overscroll for SceneB is defined + progress.value = 1.1f + assertThat(state.currentOverscrollSpec).isNotNull() + assertThat(state.currentOverscrollSpec?.scene).isEqualTo(SceneB) + } + + @Test + fun overscrollDsl_definedForFromScene() = runMonotonicClockTest { + val progress = mutableStateOf(0f) + val state = + startOverscrollableTransistionFromAtoB( + progress = { progress.value }, + sceneTransitions = + transitions { + overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) } + } + ) + assertThat(state.currentOverscrollSpec).isNull() + + // overscroll for SceneA is defined + progress.value = -0.1f + assertThat(state.currentOverscrollSpec).isNotNull() + assertThat(state.currentOverscrollSpec?.scene).isEqualTo(SceneA) + + // scroll from SceneA to SceneB + progress.value = 0.5f + assertThat(state.currentOverscrollSpec).isNull() + + progress.value = 1f + assertThat(state.currentOverscrollSpec).isNull() + + // overscroll for SceneB is NOT defined + progress.value = 1.1f + assertThat(state.currentOverscrollSpec).isNull() + } + + @Test + fun overscrollDsl_notDefinedScenes() = runMonotonicClockTest { + val progress = mutableStateOf(0f) + val state = + startOverscrollableTransistionFromAtoB( + progress = { progress.value }, + sceneTransitions = transitions {} + ) + assertThat(state.currentOverscrollSpec).isNull() + + // overscroll for SceneA is NOT defined + progress.value = -0.1f + assertThat(state.currentOverscrollSpec).isNull() + + // scroll from SceneA to SceneB + progress.value = 0.5f + assertThat(state.currentOverscrollSpec).isNull() + + progress.value = 1f + assertThat(state.currentOverscrollSpec).isNull() + + // overscroll for SceneB is NOT defined + progress.value = 1.1f + assertThat(state.currentOverscrollSpec).isNull() + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt index 1beafcc2cbb2..c9c3eccdedfc 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt @@ -20,9 +20,11 @@ import androidx.compose.animation.core.SpringSpec import androidx.compose.animation.core.TweenSpec import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween +import androidx.compose.foundation.gestures.Orientation import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.animation.scene.transformation.Transformation import com.android.compose.animation.scene.transformation.TransformationRange +import com.android.compose.animation.scene.transformation.Translate import com.google.common.truth.Correspondence import com.google.common.truth.Truth.assertThat import org.junit.Test @@ -223,6 +225,17 @@ class TransitionDslTest { .isSameInstanceAs(specFromAToC) } + @Test + fun overscrollSpec() { + val transitions = transitions { + overscroll(TestScenes.SceneA, Orientation.Vertical) { translate(TestElements.Bar) } + } + + val overscrollSpec = transitions.overscrollSpecs.single() + val transformation = overscrollSpec.transformationSpec.transformations.single() + assertThat(transformation).isInstanceOf(Translate::class.java) + } + companion object { private val TRANSFORMATION_RANGE = Correspondence.transforming<Transformation, TransformationRange?>( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/assist/data/repository/AssistRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/assist/data/repository/AssistRepositoryTest.kt new file mode 100644 index 000000000000..80077a21f985 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/assist/data/repository/AssistRepositoryTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.assist.data.repository + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class AssistRepositoryTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + + private val underTest = kosmos.assistRepository + + @Test + fun invocationType() = + testScope.runTest { + val invocationType by collectLastValue(underTest.latestInvocationType) + underTest.setLatestInvocationType(2) + runCurrent() + + assertThat(invocationType).isEqualTo(2) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/assist/domain/interactor/AssistInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/assist/domain/interactor/AssistInteractorTest.kt new file mode 100644 index 000000000000..c12f1aca350a --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/assist/domain/interactor/AssistInteractorTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.assist.domain.interactor + +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.Flags +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +@SmallTest +class AssistInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + + private val underTest = kosmos.assistInteractor + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CONTEXTUAL_TIPS, Flags.FLAG_ENABLE_CONTEXTUAL_TIP_FOR_POWER_OFF) + fun onAssistantStarted() = + testScope.runTest { + val invocationType by collectLastValue(underTest.latestInvocationType) + underTest.onAssistantStarted(3) + runCurrent() + + assertThat(invocationType).isEqualTo(3) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt index 558e7e6564ae..3a28471b5f9a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runCurrent @@ -215,6 +216,39 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() { } @Test + fun onFingerprintFailed_failedAuthenticationStatusWithOtherStatuses() = + testScope.runTest { + val failStatus by + collectLastValue( + underTest.authenticationStatus.filterIsInstance< + FailFingerprintAuthenticationStatus + >() + ) + runCurrent() + + verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture()) + updateMonitorCallback.value.onBiometricAcquired( + BiometricSourceType.FINGERPRINT, + /* acquireInfo */ 0, + ) + updateMonitorCallback.value.onBiometricAuthFailed( + BiometricSourceType.FINGERPRINT, + ) + updateMonitorCallback.value.onBiometricHelp( + /* msgId */ 7, + /* errString */ "Not recognized.", + BiometricSourceType.FINGERPRINT, + ) + updateMonitorCallback.value.onBiometricError( + /* msgId */ 7, + /* errString */ "Too many attempts.", + BiometricSourceType.FINGERPRINT, + ) + + assertThat(failStatus).isNotNull() + } + + @Test fun onFingerprintError_errorAuthenticationStatus() = testScope.runTest { val authenticationStatus by collectLastValue(underTest.authenticationStatus) diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java index d2fda4cfd186..88fa2de186d0 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java @@ -273,6 +273,10 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { } } + void onFullscreenMagnificationActivationChanged(int displayId, boolean activated) { + // Do nothing + } + @MainThread void toggleSettingsPanelVisibility(int displayId) { final MagnificationSettingsController magnificationSettingsController = diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java index ba943b07b704..b5f3aef88dc4 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java @@ -47,6 +47,12 @@ class MagnificationConnectionImpl extends IMagnificationConnection.Stub { } @Override + public void onFullscreenMagnificationActivationChanged(int displayId, boolean activated) { + mHandler.post(() -> mMagnification + .onFullscreenMagnificationActivationChanged(displayId, activated)); + } + + @Override public void enableWindowMagnification(int displayId, float scale, float centerX, float centerY, float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, IRemoteMagnificationAnimationCallback callback) { diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 74ea58caf8b6..d30f33f5ba2c 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -31,6 +31,7 @@ import com.android.internal.app.IVisualQueryRecognitionStatusListener; import com.android.internal.app.IVoiceInteractionSessionListener; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.assist.domain.interactor.AssistInteractor; import com.android.systemui.assist.ui.DefaultUiController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -149,6 +150,7 @@ public class AssistManager { private final SecureSettings mSecureSettings; private final SelectedUserInteractor mSelectedUserInteractor; private final ActivityManager mActivityManager; + private final AssistInteractor mInteractor; private final DeviceProvisionedController mDeviceProvisionedController; @@ -192,7 +194,8 @@ public class AssistManager { DisplayTracker displayTracker, SecureSettings secureSettings, SelectedUserInteractor selectedUserInteractor, - ActivityManager activityManager) { + ActivityManager activityManager, + AssistInteractor interactor) { mContext = context; mDeviceProvisionedController = controller; mCommandQueue = commandQueue; @@ -206,6 +209,7 @@ public class AssistManager { mSecureSettings = secureSettings; mSelectedUserInteractor = selectedUserInteractor; mActivityManager = activityManager; + mInteractor = interactor; registerVoiceInteractionSessionListener(); registerVisualQueryRecognitionStatusListener(); @@ -314,6 +318,7 @@ public class AssistManager { assistComponent, legacyDeviceState); logStartAssistLegacy(legacyInvocationType, legacyDeviceState); + mInteractor.onAssistantStarted(legacyInvocationType); startAssistInternal(args, assistComponent, isService); } diff --git a/packages/SystemUI/src/com/android/systemui/assist/data/repository/AssistRepository.kt b/packages/SystemUI/src/com/android/systemui/assist/data/repository/AssistRepository.kt new file mode 100644 index 000000000000..c416c249dec4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/assist/data/repository/AssistRepository.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.assist.data.repository + +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow + +@SysUISingleton +class AssistRepository @Inject constructor() { + private val _latestInvocationType = + MutableSharedFlow<Int>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + /** The type of the latest invocation of the assistant. */ + val latestInvocationType: SharedFlow<Int> = _latestInvocationType.asSharedFlow() + + /** Sets the type of the latest invocation of the assistant. */ + fun setLatestInvocationType(type: Int) { + _latestInvocationType.tryEmit(type) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/assist/domain/interactor/AssistInteractor.kt b/packages/SystemUI/src/com/android/systemui/assist/domain/interactor/AssistInteractor.kt new file mode 100644 index 000000000000..d9e46aabf539 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/assist/domain/interactor/AssistInteractor.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.assist.domain.interactor + +import com.android.systemui.Flags +import com.android.systemui.assist.data.repository.AssistRepository +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.SharedFlow + +@SysUISingleton +class AssistInteractor +@Inject +constructor( + private val repository: AssistRepository, +) { + /** The type of the latest invocation of the assistant. */ + val latestInvocationType: SharedFlow<Int> = repository.latestInvocationType + + /** Notifies that Assistant has been started. */ + fun onAssistantStarted(type: Int) { + if (Flags.enableContextualTips() && Flags.enableContextualTipForPowerOff()) { + repository.setLatestInvocationType(type) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt index ae1539ebaf89..59b59bf1e437 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt @@ -50,6 +50,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext @@ -64,13 +65,16 @@ interface FacePropertyRepository { /** The current face sensor location in current device rotation */ val sensorLocation: StateFlow<Point?> + + /** The info of current available camera. */ + val cameraInfo: StateFlow<CameraInfo?> } /** Describes a biometric sensor */ data class FaceSensorInfo(val id: Int, val strength: SensorStrength) /** Data class for camera info */ -private data class CameraInfo( +data class CameraInfo( /** The logical id of the camera */ val cameraId: String, /** The physical id of the camera */ @@ -124,7 +128,7 @@ constructor( private val cameraInfoList: List<CameraInfo> = loadCameraInfoList() private var currentPhysicalCameraId: String? = null - private val defaultSensorLocation: StateFlow<Point?> = + override val cameraInfo: StateFlow<CameraInfo?> = ConflatedCallbackFlow.conflatedCallbackFlow { val callback = object : CameraManager.AvailabilityCallback() { @@ -142,7 +146,7 @@ constructor( physicalCameraId == it.cameraPhysicalId } trySendWithFailureLogging( - cameraInfo?.cameraLocation, + cameraInfo, TAG, "Update face sensor location to $cameraInfo." ) @@ -168,7 +172,7 @@ constructor( } currentPhysicalCameraId = cameraInfo?.cameraPhysicalId trySendWithFailureLogging( - cameraInfo?.cameraLocation, + cameraInfo, TAG, "Update face sensor location to $cameraInfo." ) @@ -181,8 +185,16 @@ constructor( .stateIn( applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = - if (cameraInfoList.isNotEmpty()) cameraInfoList[0].cameraLocation else null + initialValue = if (cameraInfoList.isNotEmpty()) cameraInfoList[0] else null + ) + + private val defaultSensorLocation: StateFlow<Point?> = + cameraInfo + .map { it?.cameraLocation } + .stateIn( + applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = null ) override val sensorLocation: StateFlow<Point?> = diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt index cf91e147d1b3..0c9fbc27cc5d 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt @@ -189,6 +189,18 @@ constructor( } } .launchIn(applicationScope) + + facePropertyRepository.cameraInfo + .onEach { + if (it != null && isRunning()) { + repository.cancel() + runFaceAuth( + FaceAuthUiEvent.FACE_AUTH_CAMERA_AVAILABLE_CHANGED, + fallbackToDetect = true + ) + } + } + .launchIn(applicationScope) } private suspend fun resetLockedOutState(currentUserId: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt index ee220d5fb713..08a6166fe557 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt @@ -31,6 +31,7 @@ import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALTERNATE import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.BIOMETRIC_ENABLED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.CAMERA_AVAILABLE_CHANGED import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.CAMERA_LAUNCHED import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DISPLAY_OFF @@ -130,6 +131,7 @@ private object InternalFaceAuthReasons { "Face auth stopped because non strong biometric allowed changed" const val POSTURE_CHANGED = "Face auth started/stopped due to device posture changed." const val DISPLAY_OFF = "Face auth stopped due to display state OFF." + const val CAMERA_AVAILABLE_CHANGED = "Face auth started due to the available camera changed" } /** @@ -221,7 +223,9 @@ constructor(private val id: Int, val reason: String, var extraInfo: Int = 0) : @UiEvent(doc = NON_STRONG_BIOMETRIC_ALLOWED_CHANGED) FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED(1256, NON_STRONG_BIOMETRIC_ALLOWED_CHANGED), @UiEvent(doc = ACCESSIBILITY_ACTION) FACE_AUTH_ACCESSIBILITY_ACTION(1454, ACCESSIBILITY_ACTION), - @UiEvent(doc = DISPLAY_OFF) FACE_AUTH_DISPLAY_OFF(1461, DISPLAY_OFF); + @UiEvent(doc = DISPLAY_OFF) FACE_AUTH_DISPLAY_OFF(1461, DISPLAY_OFF), + @UiEvent(doc = CAMERA_AVAILABLE_CHANGED) + FACE_AUTH_CAMERA_AVAILABLE_CHANGED(1623, CAMERA_AVAILABLE_CHANGED); override fun getId(): Int = this.id diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index c6b99528b2ba..6d917bbde82b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -40,6 +40,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE; import static com.android.systemui.DejankUtils.whitelistIpcs; +import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground; import static com.android.systemui.Flags.refactorGetCurrentUser; import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; @@ -1488,7 +1489,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } public void userActivity() { - mPM.userActivity(mSystemClock.uptimeMillis(), false); + if (notifyPowerManagerUserActivityBackground()) { + mUiBgExecutor.execute(() -> mPM.userActivity(mSystemClock.uptimeMillis(), false)); + } else { + mPM.userActivity(mSystemClock.uptimeMillis(), false); + } } private void setupLocked() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt index b152eea63028..2bede977c958 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.shareIn @@ -177,92 +178,99 @@ constructor( // TODO(b/322555228) Remove after consolidating device entry auth messages with BP auth messages // in BiometricStatusRepository + /** + * FingerprintAuthenticationStatus Multiple statuses may arrive in immediate sequence (ie: + * acquired, failed, help, error), so we use a buffer to ensure consumers receive each distinct + * status. + */ override val authenticationStatus: Flow<FingerprintAuthenticationStatus> - get() = conflatedCallbackFlow { - val callback = - object : KeyguardUpdateMonitorCallback() { - override fun onBiometricAuthenticated( - userId: Int, - biometricSourceType: BiometricSourceType, - isStrongBiometric: Boolean, - ) { - sendUpdateIfFingerprint( - biometricSourceType, - SuccessFingerprintAuthenticationStatus( - userId, - isStrongBiometric, - ), - ) - } + get() = + conflatedCallbackFlow { + val callback = + object : KeyguardUpdateMonitorCallback() { + override fun onBiometricAuthenticated( + userId: Int, + biometricSourceType: BiometricSourceType, + isStrongBiometric: Boolean, + ) { + sendUpdateIfFingerprint( + biometricSourceType, + SuccessFingerprintAuthenticationStatus( + userId, + isStrongBiometric, + ), + ) + } - override fun onBiometricError( - msgId: Int, - errString: String?, - biometricSourceType: BiometricSourceType, - ) { - sendUpdateIfFingerprint( - biometricSourceType, - ErrorFingerprintAuthenticationStatus( - msgId, - errString, - ), - ) - } + override fun onBiometricError( + msgId: Int, + errString: String?, + biometricSourceType: BiometricSourceType, + ) { + sendUpdateIfFingerprint( + biometricSourceType, + ErrorFingerprintAuthenticationStatus( + msgId, + errString, + ), + ) + } - override fun onBiometricHelp( - msgId: Int, - helpString: String?, - biometricSourceType: BiometricSourceType, - ) { - sendUpdateIfFingerprint( - biometricSourceType, - HelpFingerprintAuthenticationStatus( - msgId, - helpString, - ), - ) - } + override fun onBiometricHelp( + msgId: Int, + helpString: String?, + biometricSourceType: BiometricSourceType, + ) { + sendUpdateIfFingerprint( + biometricSourceType, + HelpFingerprintAuthenticationStatus( + msgId, + helpString, + ), + ) + } - override fun onBiometricAuthFailed( - biometricSourceType: BiometricSourceType, - ) { - sendUpdateIfFingerprint( - biometricSourceType, - FailFingerprintAuthenticationStatus, - ) - } + override fun onBiometricAuthFailed( + biometricSourceType: BiometricSourceType, + ) { + sendUpdateIfFingerprint( + biometricSourceType, + FailFingerprintAuthenticationStatus, + ) + } - override fun onBiometricAcquired( - biometricSourceType: BiometricSourceType, - acquireInfo: Int, - ) { - sendUpdateIfFingerprint( - biometricSourceType, - AcquiredFingerprintAuthenticationStatus( - AuthenticationReason.DeviceEntryAuthentication, - acquireInfo - ), - ) - } + override fun onBiometricAcquired( + biometricSourceType: BiometricSourceType, + acquireInfo: Int, + ) { + sendUpdateIfFingerprint( + biometricSourceType, + AcquiredFingerprintAuthenticationStatus( + AuthenticationReason.DeviceEntryAuthentication, + acquireInfo + ), + ) + } - private fun sendUpdateIfFingerprint( - biometricSourceType: BiometricSourceType, - authenticationStatus: FingerprintAuthenticationStatus - ) { - if (biometricSourceType != BiometricSourceType.FINGERPRINT) { - return - } + private fun sendUpdateIfFingerprint( + biometricSourceType: BiometricSourceType, + authenticationStatus: FingerprintAuthenticationStatus + ) { + if (biometricSourceType != BiometricSourceType.FINGERPRINT) { + return + } - trySendWithFailureLogging( - authenticationStatus, - TAG, - "new fingerprint authentication status" - ) - } + trySendWithFailureLogging( + authenticationStatus, + TAG, + "new fingerprint authentication status" + ) + } + } + keyguardUpdateMonitor.registerCallback(callback) + awaitClose { keyguardUpdateMonitor.removeCallback(callback) } } - keyguardUpdateMonitor.registerCallback(callback) - awaitClose { keyguardUpdateMonitor.removeCallback(callback) } - } + .buffer(capacity = 4) override val shouldUpdateIndicatorVisibility: Flow<Boolean> = conflatedCallbackFlow { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java index 4e940f1f84da..840b309aee39 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java @@ -18,7 +18,7 @@ package com.android.systemui.media.controls.ui.controller; import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS; -import static com.android.systemui.Flags.legacyLeAudioSharing; +import static com.android.settingslib.flags.Flags.legacyLeAudioSharing; import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS; import android.animation.Animator; diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java index 8e0191ec330f..1e317554859c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java @@ -16,6 +16,8 @@ package com.android.systemui.media.dialog; +import static com.android.settingslib.flags.Flags.legacyLeAudioSharing; + import android.app.AlertDialog; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastAssistant; @@ -492,6 +494,7 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { @Override public boolean isBroadcastSupported() { + if (!legacyLeAudioSharing()) return false; boolean isBluetoothLeDevice = false; if (mMediaOutputController.getCurrentConnectedMediaDevice() != null) { isBluetoothLeDevice = mMediaOutputController.isBluetoothLeDevice( diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java index c379d0e68e76..eb6a32023eb7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java @@ -16,7 +16,7 @@ package com.android.systemui.media.dialog; -import static com.android.systemui.Flags.legacyLeAudioSharing; +import static com.android.settingslib.flags.Flags.legacyLeAudioSharing; import android.content.Context; import android.os.Bundle; diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt index 1002cc3bd3bb..38d31ed92141 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt @@ -16,6 +16,7 @@ package com.android.systemui.media.dialog +import com.android.settingslib.flags.Flags.legacyLeAudioSharing import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -44,6 +45,7 @@ class MediaOutputDialogReceiver @Inject constructor( mediaOutputDialogFactory.createDialogForSystemRouting() } MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG -> { + if (!legacyLeAudioSharing()) return val packageName: String? = intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME) launchMediaOutputBroadcastDialogIfPossible(packageName) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt index afe628527833..f1cade7512e2 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt @@ -16,9 +16,6 @@ package com.android.systemui.mediaprojection -import android.compat.annotation.ChangeId -import android.compat.annotation.Disabled -import android.compat.annotation.Overridable import android.content.Context import android.media.projection.IMediaProjection import android.media.projection.IMediaProjectionManager @@ -34,18 +31,6 @@ import android.util.Log */ class MediaProjectionServiceHelper { companion object { - /** - * This change id ensures that users are presented with a choice of capturing a single app - * or the entire screen when initiating a MediaProjection session, overriding the usage of - * MediaProjectionConfig#createConfigForDefaultDisplay. - * - * @hide - */ - @ChangeId - @Overridable - @Disabled - const val OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION = 316897322L // buganizer id - private const val TAG = "MediaProjectionServiceHelper" private val service = IMediaProjectionManager.Stub.asInterface( diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java index 0769731bffb1..8b034b293dcb 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java @@ -16,26 +16,21 @@ package com.android.systemui.mediaprojection.permission; -import static android.Manifest.permission.LOG_COMPAT_CHANGE; -import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; import static android.media.projection.IMediaProjectionManager.EXTRA_PACKAGE_REUSING_GRANTED_CONSENT; import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTED_CONSENT; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; -import static com.android.systemui.mediaprojection.MediaProjectionServiceHelper.OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION; import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN; import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.SINGLE_APP; import android.annotation.Nullable; -import android.annotation.RequiresPermission; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityOptions.LaunchCookie; import android.app.AlertDialog; import android.app.StatusBarManager; -import android.app.compat.CompatChanges; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -109,7 +104,6 @@ public class MediaProjectionPermissionActivity extends Activity } @Override - @RequiresPermission(allOf = {READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE}) public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -237,9 +231,6 @@ public class MediaProjectionPermissionActivity extends Activity // the correct screen width when in split screen. Context dialogContext = getApplicationContext(); if (isPartialScreenSharingEnabled()) { - final boolean overrideDisableSingleAppOption = CompatChanges.isChangeEnabled( - OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION, - mPackageName, getHostUserHandle()); MediaProjectionPermissionDialogDelegate delegate = new MediaProjectionPermissionDialogDelegate( dialogContext, @@ -251,7 +242,6 @@ public class MediaProjectionPermissionActivity extends Activity }, () -> finish(RECORD_CANCEL, /* projection= */ null), appName, - overrideDisableSingleAppOption, mUid, mMediaProjectionMetricsLogger); mDialog = diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt index 9ce8070131fa..0f54e934f3cf 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt @@ -30,12 +30,11 @@ class MediaProjectionPermissionDialogDelegate( private val onStartRecordingClicked: Consumer<MediaProjectionPermissionDialogDelegate>, private val onCancelClicked: Runnable, private val appName: String?, - forceShowPartialScreenshare: Boolean, hostUid: Int, mediaProjectionMetricsLogger: MediaProjectionMetricsLogger, ) : BaseMediaProjectionPermissionDialogDelegate<AlertDialog>( - createOptionList(context, appName, mediaProjectionConfig, forceShowPartialScreenshare), + createOptionList(context, appName, mediaProjectionConfig), appName, hostUid, mediaProjectionMetricsLogger @@ -66,8 +65,7 @@ class MediaProjectionPermissionDialogDelegate( private fun createOptionList( context: Context, appName: String?, - mediaProjectionConfig: MediaProjectionConfig?, - overrideDisableSingleAppOption: Boolean = false, + mediaProjectionConfig: MediaProjectionConfig? ): List<ScreenShareOption> { val singleAppWarningText = if (appName == null) { @@ -82,13 +80,8 @@ class MediaProjectionPermissionDialogDelegate( R.string.media_projection_entry_app_permission_dialog_warning_entire_screen } - // The single app option should only be disabled if there is an app name provided, - // the client has setup a MediaProjection with - // MediaProjectionConfig#createConfigForDefaultDisplay, AND it hasn't been overridden by - // the OVERRIDE_DISABLE_SINGLE_APP_OPTION per-app override. val singleAppOptionDisabled = appName != null && - !overrideDisableSingleAppOption && mediaProjectionConfig?.regionToCapture == MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 9052409b4d8d..a1fae03a2090 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -39,11 +39,11 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityTransitionAnimator; +import com.android.systemui.animation.RemoteAnimationRunnerCompat; import com.android.systemui.display.data.repository.DisplayMetricsRepository; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.qs.QSPanelController; -import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.util.Compile; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt index 9f24d5dfea79..f5e96c93271a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt @@ -237,6 +237,35 @@ class FacePropertyRepositoryImplTest : SysuiTestCase() { } } + @Test + fun providesTheCameraInfoOnCameraAvailableChange() { + testScope.runTest { + runCurrent() + collectLastValue(underTest.cameraInfo) + + verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture()) + callback.value.onAllAuthenticatorsRegistered( + listOf(createSensorProperties(1, SensorProperties.STRENGTH_STRONG)) + ) + runCurrent() + verify(cameraManager) + .registerAvailabilityCallback(any(Executor::class.java), cameraCallback.capture()) + + cameraCallback.value.onPhysicalCameraAvailable("0", PHYSICAL_CAMERA_ID_OUTER_FRONT) + runCurrent() + + val cameraInfo by collectLastValue(underTest.cameraInfo) + assertThat(cameraInfo) + .isEqualTo( + CameraInfo( + "0", + PHYSICAL_CAMERA_ID_OUTER_FRONT, + Point(OUTER_FRONT_SENSOR_LOCATION[0], OUTER_FRONT_SENSOR_LOCATION[1]) + ) + ) + } + } + private fun createSensorProperties(id: Int, strength: Int) = FaceSensorPropertiesInternal(id, strength, 0, emptyList(), 1, false, false, false) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt index e30dd35d74c1..e1e9fcb2d059 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt @@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest import com.android.keyguard.keyguardUpdateMonitor import com.android.keyguard.trustManager import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.CameraInfo import com.android.systemui.biometrics.data.repository.FaceSensorInfo import com.android.systemui.biometrics.data.repository.facePropertyRepository import com.android.systemui.biometrics.shared.model.LockoutMode @@ -490,6 +491,47 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() { verify(trustManager).clearAllBiometricRecognized(eq(BiometricSourceType.FACE), anyInt()) } + @Test + fun faceAuthIsRequestedWhenAuthIsRunningWhileCameraInfoChanged() = + testScope.runTest { + facePropertyRepository.setCameraIno(null) + underTest.start() + + faceAuthRepository.requestAuthenticate( + FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED, + true + ) + facePropertyRepository.setCameraIno(CameraInfo("0", "1", null)) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_CAMERA_AVAILABLE_CHANGED, true)) + } + + @Test + fun faceAuthIsNotRequestedWhenNoAuthRunningWhileCameraInfoChanged() = + testScope.runTest { + facePropertyRepository.setCameraIno(null) + underTest.start() + + facePropertyRepository.setCameraIno(CameraInfo("0", "1", null)) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value).isNull() + } + + @Test + fun faceAuthIsNotRequestedWhenAuthIsRunningWhileCameraInfoIsNull() = + testScope.runTest { + facePropertyRepository.setCameraIno(null) + underTest.start() + + facePropertyRepository.setCameraIno(null) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value).isNull() + } + companion object { private const val primaryUserId = 1 private val primaryUser = UserInfo(primaryUserId, "test user", UserInfo.FLAG_PRIMARY) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt index 2e7829d4ea7b..2f92afa39b52 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt @@ -1247,7 +1247,7 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test - @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(com.android.settingslib.flags.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) fun bindBroadcastButton() { initMediaViewHolderMocks() initDeviceMediaData(true, APP_NAME) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java index e2cf87a6dd62..087988469a49 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java @@ -28,6 +28,7 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.settingslib.flags.Flags; import com.android.settingslib.media.MediaOutputConstants; import com.android.systemui.SysuiTestCase; @@ -84,7 +85,20 @@ public class MediaOutputDialogReceiverTest extends SysuiTestCase { } @Test + public void launchMediaOutputBroadcastDialog_flagOff_broadcastDialogFactoryNotCalled() { + mSetFlagsRule.disableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING); + Intent intent = new Intent( + MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG); + intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName()); + mMediaOutputDialogReceiver.onReceive(getContext(), intent); + + verify(mMockMediaOutputDialogFactory, never()).create(any(), anyBoolean(), any()); + verify(mMockMediaOutputBroadcastDialogFactory, never()).create(any(), anyBoolean(), any()); + } + + @Test public void launchMediaOutputBroadcastDialog_ExtraPackageName_BroadcastDialogFactoryCalled() { + mSetFlagsRule.enableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING); Intent intent = new Intent( MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG); intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName()); @@ -97,6 +111,7 @@ public class MediaOutputDialogReceiverTest extends SysuiTestCase { @Test public void launchMediaOutputBroadcastDialog_WrongExtraKey_DialogBroadcastFactoryNotCalled() { + mSetFlagsRule.enableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING); Intent intent = new Intent( MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG); intent.putExtra("Wrong Package Name Key", getContext().getPackageName()); @@ -108,6 +123,7 @@ public class MediaOutputDialogReceiverTest extends SysuiTestCase { @Test public void launchMediaOutputBroadcastDialog_NoExtra_BroadcastDialogFactoryNotCalled() { + mSetFlagsRule.enableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING); Intent intent = new Intent( MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG); mMediaOutputDialogReceiver.onReceive(getContext(), intent); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java index d9ddc8e6b23b..84300da82a30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java @@ -50,6 +50,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.flags.Flags; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.systemui.SysuiTestCase; @@ -177,7 +178,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void getStopButtonVisibility_remoteBLEDevice_returnVisible() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -189,7 +190,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void getStopButtonVisibility_remoteNonBLEDevice_returnGone() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -210,7 +211,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void isBroadcastSupported_flagOnAndConnectBleDevice_returnsTrue() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -223,7 +224,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void isBroadcastSupported_flagOnAndNoBleDevice_returnsFalse() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -236,7 +237,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void isBroadcastSupported_notSupportBroadcastAndflagOn_returnsFalse() { FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); @@ -245,7 +246,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void isBroadcastSupported_flagOffAndConnectToBleDevice_returnsTrue() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -258,7 +259,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void isBroadcastSupported_flagOffAndNoBleDevice_returnsTrue() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -271,7 +272,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void isBroadcastSupported_noBleDeviceAndEnabledBroadcast_returnsTrue() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -284,7 +285,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void isBroadcastSupported_noBleDeviceAndDisabledBroadcast_returnsFalse() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -297,7 +298,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void getBroadcastIconVisibility_isBroadcasting_returnVisible() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -309,7 +310,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void getBroadcastIconVisibility_noBroadcasting_returnGone() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -321,7 +322,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void getBroadcastIconVisibility_remoteNonLeDevice_returnGone() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); @@ -374,7 +375,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING) + @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING) public void getStopButtonText_supportsBroadcast_returnsBroadcastText() { String stopText = mContext.getText(R.string.media_output_broadcast).toString(); MediaDevice mMediaDevice = mock(MediaDevice.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt deleted file mode 100644 index e044eeca8303..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2022 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.mediaprojection.permission - -import android.app.AlertDialog -import android.media.projection.MediaProjectionConfig -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import android.view.WindowManager -import android.widget.Spinner -import android.widget.TextView -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FeatureFlagsClassic -import com.android.systemui.flags.Flags -import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger -import com.android.systemui.res.R -import com.android.systemui.statusbar.phone.AlertDialogWithDelegate -import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.util.mockito.mock -import junit.framework.Assert.assertEquals -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.`when` as whenever - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() { - - private lateinit var dialog: AlertDialog - - private val flags = mock<FeatureFlagsClassic>() - private val onStartRecordingClicked = mock<Runnable>() - private val mediaProjectionMetricsLogger = mock<MediaProjectionMetricsLogger>() - - private val mediaProjectionConfig: MediaProjectionConfig = - MediaProjectionConfig.createConfigForDefaultDisplay() - private val appName: String = "testApp" - private val hostUid: Int = 12345 - - private val resIdSingleApp = R.string.screen_share_permission_dialog_option_single_app - private val resIdFullScreen = R.string.screen_share_permission_dialog_option_entire_screen - private val resIdSingleAppDisabled = - R.string.media_projection_entry_app_permission_dialog_single_app_disabled - - @Before - fun setUp() { - whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true) - } - - @After - fun teardown() { - if (::dialog.isInitialized) { - dialog.dismiss() - } - } - - @Test - fun showDialog_forceShowPartialScreenShareFalse() { - // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and - // overrideDisableSingleAppOption = false - val overrideDisableSingleAppOption = false - setUpAndShowDialog(overrideDisableSingleAppOption) - - val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner) - val secondOptionText = - spinner.adapter - .getDropDownView(1, null, spinner) - .findViewById<TextView>(android.R.id.text2) - ?.text - - // check that the first option is full screen and enabled - assertEquals(context.getString(resIdFullScreen), spinner.selectedItem) - - // check that the second option is single app and disabled - assertEquals(context.getString(resIdSingleAppDisabled, appName), secondOptionText) - } - - @Test - fun showDialog_forceShowPartialScreenShareTrue() { - // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and - // overrideDisableSingleAppOption = true - val overrideDisableSingleAppOption = true - setUpAndShowDialog(overrideDisableSingleAppOption) - - val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner) - val secondOptionText = - spinner.adapter - .getDropDownView(1, null, spinner) - .findViewById<TextView>(android.R.id.text1) - ?.text - - // check that the first option is single app and enabled - assertEquals(context.getString(resIdSingleApp), spinner.selectedItem) - - // check that the second option is full screen and enabled - assertEquals(context.getString(resIdFullScreen), secondOptionText) - } - - private fun setUpAndShowDialog(overrideDisableSingleAppOption: Boolean) { - val delegate = - MediaProjectionPermissionDialogDelegate( - context, - mediaProjectionConfig, - {}, - onStartRecordingClicked, - appName, - overrideDisableSingleAppOption, - hostUid, - mediaProjectionMetricsLogger - ) - - dialog = AlertDialogWithDelegate(context, R.style.Theme_SystemUI_Dialog, delegate) - SystemUIDialog.applyFlags(dialog) - SystemUIDialog.setDialogSize(dialog) - - dialog.window?.addSystemFlags( - WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS - ) - - delegate.onCreate(dialog, savedInstanceState = null) - dialog.show() - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java index 460892af1154..660e8da72bf1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java @@ -49,6 +49,7 @@ import android.window.TransitionInfo; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.RemoteAnimationTargetCompat; import com.android.wm.shell.shared.TransitionUtil; import org.junit.Before; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/assist/data/repository/AssistRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/assist/data/repository/AssistRepositoryKosmos.kt new file mode 100644 index 000000000000..96155ed1ec9f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/assist/data/repository/AssistRepositoryKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.assist.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.assistRepository by Kosmos.Fixture { AssistRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/assist/domain/interactor/AssistInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/assist/domain/interactor/AssistInteractorKosmos.kt new file mode 100644 index 000000000000..c3c11318b0cc --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/assist/domain/interactor/AssistInteractorKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.assist.domain.interactor + +import com.android.systemui.assist.data.repository.assistRepository +import com.android.systemui.kosmos.Kosmos + +val Kosmos.assistInteractor by Kosmos.Fixture { AssistInteractor(assistRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt index 77f501f550d7..68ef55573dc8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt @@ -33,6 +33,10 @@ class FakeFacePropertyRepository : FacePropertyRepository { override val sensorLocation: StateFlow<Point?> get() = faceSensorLocation + private val currentCameraInfo = MutableStateFlow<CameraInfo?>(null) + override val cameraInfo: StateFlow<CameraInfo?> + get() = currentCameraInfo + fun setLockoutMode(userId: Int, mode: LockoutMode) { lockoutModesForUser[userId] = mode } @@ -47,4 +51,8 @@ class FakeFacePropertyRepository : FacePropertyRepository { fun setSensorLocation(value: Point?) { faceSensorLocation.value = value } + + fun setCameraIno(value: CameraInfo?) { + currentCameraInfo.value = value + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 46db624cf3c1..43c018cfeea3 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3460,13 +3460,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (!mMagnificationController.supportWindowMagnification()) { return; } - final boolean connect = (userState.isShortcutMagnificationEnabledLocked() + + final boolean shortcutEnabled = (userState.isShortcutMagnificationEnabledLocked() || userState.isMagnificationSingleFingerTripleTapEnabledLocked() || (Flags.enableMagnificationMultipleFingerMultipleTapGesture() - && userState.isMagnificationTwoFingerTripleTapEnabledLocked())) - && (userState.getMagnificationCapabilitiesLocked() - != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) + && userState.isMagnificationTwoFingerTripleTapEnabledLocked())); + + final boolean createConnectionForCurrentCapability = + com.android.window.flags.Flags.magnificationAlwaysDrawFullscreenBorder() + || (userState.getMagnificationCapabilitiesLocked() + != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + + final boolean connect = (shortcutEnabled && createConnectionForCurrentCapability) || userHasMagnificationServicesLocked(userState); + getMagnificationConnectionManager().requestConnection(connect); } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java index e11c36a91830..bc143428e1ae 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java @@ -674,6 +674,23 @@ public class MagnificationConnectionManager implements } /** + * Notify Fullscreen magnification activation changes. + */ + public boolean onFullscreenMagnificationActivationChanged(int displayId, boolean activated) { + synchronized (mLock) { + waitForConnectionIfNeeded(); + if (mConnectionWrapper == null) { + Slog.w(TAG, + "onFullscreenMagnificationActivationChanged mConnectionWrapper is null. " + + "mConnectionState=" + connectionStateToString(mConnectionState)); + return false; + } + return mConnectionWrapper + .onFullscreenMagnificationActivationChanged(displayId, activated); + } + } + + /** * Calculates the number of fingers in the window. * * @param displayId The logical display id. @@ -1267,15 +1284,7 @@ public class MagnificationConnectionManager implements float centerY, float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, MagnificationAnimationCallback animationCallback) { - // Wait for the connection with a timeout. - final long endMillis = SystemClock.uptimeMillis() + WAIT_CONNECTION_TIMEOUT_MILLIS; - while (mConnectionState == CONNECTING && (SystemClock.uptimeMillis() < endMillis)) { - try { - mLock.wait(endMillis - SystemClock.uptimeMillis()); - } catch (InterruptedException ie) { - /* ignore */ - } - } + waitForConnectionIfNeeded(); if (mConnectionWrapper == null) { Slog.w(TAG, "enableWindowMagnificationInternal mConnectionWrapper is null. " @@ -1317,4 +1326,16 @@ public class MagnificationConnectionManager implements return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifierToPosition( displayId, positionX, positionY, animationCallback); } + + private void waitForConnectionIfNeeded() { + // Wait for the connection with a timeout. + final long endMillis = SystemClock.uptimeMillis() + WAIT_CONNECTION_TIMEOUT_MILLIS; + while (mConnectionState == CONNECTING && (SystemClock.uptimeMillis() < endMillis)) { + try { + mLock.wait(endMillis - SystemClock.uptimeMillis()); + } catch (InterruptedException ie) { + /* ignore */ + } + } + } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java index db5b3133169a..f6fb24f38a7a 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java @@ -58,6 +58,22 @@ class MagnificationConnectionWrapper { mConnection.asBinder().linkToDeath(deathRecipient, 0); } + boolean onFullscreenMagnificationActivationChanged(int displayId, boolean activated) { + if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) { + mTrace.logTrace(TAG + ".onFullscreenMagnificationActivationChanged", + FLAGS_MAGNIFICATION_CONNECTION); + } + try { + mConnection.onFullscreenMagnificationActivationChanged(displayId, activated); + } catch (RemoteException e) { + if (DBG) { + Slog.e(TAG, "Error calling onFullscreenMagnificationActivationChanged"); + } + return false; + } + return true; + } + boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY, float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, @Nullable MagnificationAnimationCallback callback) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 52e123a5e70c..0d5fd1435ca0 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -50,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.wm.WindowManagerInternal; +import com.android.window.flags.Flags; import java.util.concurrent.Executor; @@ -586,6 +587,11 @@ public class MagnificationController implements MagnificationConnectionManager.C @Override public void onFullScreenMagnificationActivationState(int displayId, boolean activated) { + if (Flags.magnificationAlwaysDrawFullscreenBorder()) { + getMagnificationConnectionManager() + .onFullscreenMagnificationActivationChanged(displayId, activated); + } + if (activated) { synchronized (mLock) { mFullScreenModeEnabledTimeArray.put(displayId, SystemClock.uptimeMillis()); diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index c4341b9367ec..c7b844b991c4 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -193,6 +193,7 @@ import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import java.io.PrintWriter; +import java.io.Serializable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -2802,9 +2803,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId( authenticationId); + Dataset dataset = null; // Authenticated a dataset - reset view state regardless if we got a response or a dataset if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) { - final Dataset dataset = authenticatedResponse.getDatasets().get(datasetIdx); + dataset = authenticatedResponse.getDatasets().get(datasetIdx); if (dataset == null) { Slog.w(TAG, "no dataset with index " + datasetIdx + " on fill response"); mPresentationStatsEventLogger.maybeSetAuthenticationResult( @@ -2819,12 +2821,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionFlags.mExpiredResponse = false; final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT); + final Serializable exception = data.getSerializable( + CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION); final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE); if (sDebug) { Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result + ", clientState=" + newClientState + ", authenticationId=" + authenticationId); } + if (Flags.autofillCredmanDevIntegration() && exception != null + && exception instanceof GetCredentialException) { + if (dataset != null && dataset.getFieldIds().size() == 1) { + if (sDebug) { + Slog.d(TAG, "setAuthenticationResultLocked(): result returns with" + + "Credential Manager Exception"); + } + AutofillId autofillId = dataset.getFieldIds().get(0); + sendCredentialManagerResponseToApp(/*response=*/ null, + (GetCredentialException) exception, autofillId); + } + return; + } + if (result instanceof FillResponse) { if (sDebug) { Slog.d(TAG, "setAuthenticationResultLocked(): received FillResponse from" @@ -2840,13 +2858,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (Flags.autofillCredmanDevIntegration()) { GetCredentialResponse response = (GetCredentialResponse) result; - sendCredentialManagerResponseToApp(response, - /*exception=*/ null, response.getAutofillId()); + if (dataset != null && dataset.getFieldIds().size() == 1) { + AutofillId autofillId = dataset.getFieldIds().get(0); + if (sDebug) { + Slog.d(TAG, "Received GetCredentialResponse from authentication flow," + + "for autofillId: " + autofillId); + } + sendCredentialManagerResponseToApp(response, + /*exception=*/ null, autofillId); + } } else if (Flags.autofillCredmanIntegration()) { - Dataset dataset = getDatasetFromCredentialResponse( + Dataset datasetFromCredentialResponse = getDatasetFromCredentialResponse( (GetCredentialResponse) result); - if (dataset != null) { - autoFill(requestId, datasetIdx, dataset, false, UI_TYPE_UNKNOWN); + if (datasetFromCredentialResponse != null) { + autoFill(requestId, datasetIdx, datasetFromCredentialResponse, + false, UI_TYPE_UNKNOWN); } } } else if (result instanceof Dataset) { @@ -2863,12 +2889,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Slog.d(TAG, "Updating client state from auth dataset"); mClientState = newClientState; } - Dataset dataset = getEffectiveDatasetForAuthentication((Dataset) result); + Dataset datasetFromResult = getEffectiveDatasetForAuthentication((Dataset) result); final Dataset oldDataset = authenticatedResponse.getDatasets().get(datasetIdx); if (!isAuthResultDatasetEphemeral(oldDataset, data)) { - authenticatedResponse.getDatasets().set(datasetIdx, dataset); + authenticatedResponse.getDatasets().set(datasetIdx, datasetFromResult); } - autoFill(requestId, datasetIdx, dataset, false, UI_TYPE_UNKNOWN); + autoFill(requestId, datasetIdx, datasetFromResult, false, UI_TYPE_UNKNOWN); } else { Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id " + authenticationId); @@ -5052,12 +5078,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } private void addCredentialManagerCallbackForDataset(Dataset dataset, int requestId) { + AutofillId autofillId = null; + if (dataset != null && dataset.getFieldIds().size() == 1) { + autofillId = dataset.getFieldIds().get(0); + } + final AutofillId finalAutofillId = autofillId; final ResultReceiver resultReceiver = new ResultReceiver(mHandler) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode == SUCCESS_CREDMAN_SELECTOR) { Slog.d(TAG, "onReceiveResult from Credential Manager bottom sheet"); - boolean isCredmanCallbackInvoked = false; GetCredentialResponse getCredentialResponse = resultData.getParcelable( CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, @@ -5065,7 +5095,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (Flags.autofillCredmanDevIntegration()) { sendCredentialManagerResponseToApp(getCredentialResponse, - /*exception=*/ null, getCredentialResponse.getAutofillId()); + /*exception=*/ null, finalAutofillId); } else { Dataset datasetFromCredential = getDatasetFromCredentialResponse( getCredentialResponse); @@ -5082,7 +5112,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.w(TAG, "Credman bottom sheet from pinned " + "entry failed with: + " + exception[0] + " , " + exception[1]); - // TODO(b/326313420): Propagate exception + sendCredentialManagerResponseToApp(/*response=*/ null, + new GetCredentialException(exception[0], exception[1]), + finalAutofillId); } } else { Slog.d(TAG, "Unknown resultCode from credential " @@ -6380,7 +6412,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } if (exception != null) { - // TODO(b/326313420): Add Exception support + mClient.onGetCredentialException(id, viewId, exception.getType(), + exception.getMessage()); } else if (response != null) { mClient.onGetCredentialResponse(id, viewId, response); } else { diff --git a/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java b/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java new file mode 100644 index 000000000000..de6382e316df --- /dev/null +++ b/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.companion; + +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; +import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION; + +import static com.android.internal.util.CollectionUtils.any; +import static com.android.server.companion.MetricUtils.logRemoveAssociation; +import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation; +import static com.android.server.companion.CompanionDeviceManagerService.PerUserAssociationSet; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.companion.AssociationInfo; +import android.content.Context; +import android.content.pm.PackageManagerInternal; +import android.os.Binder; +import android.os.UserHandle; +import android.util.ArraySet; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.companion.datatransfer.SystemDataTransferRequestStore; +import com.android.server.companion.presence.CompanionDevicePresenceMonitor; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * A class response for Association removal. + */ +@SuppressLint("LongLogTag") +public class AssociationRevokeProcessor { + + private static final String TAG = "CDM_AssociationRevokeProcessor"; + private static final boolean DEBUG = false; + private final @NonNull Context mContext; + private final @NonNull CompanionDeviceManagerService mService; + private final @NonNull AssociationStoreImpl mAssociationStore; + private final @NonNull PackageManagerInternal mPackageManagerInternal; + private final @NonNull CompanionDevicePresenceMonitor mDevicePresenceMonitor; + private final @NonNull SystemDataTransferRequestStore mSystemDataTransferRequestStore; + private final @NonNull CompanionApplicationController mCompanionAppController; + private final OnPackageVisibilityChangeListener mOnPackageVisibilityChangeListener; + private final ActivityManager mActivityManager; + + /** + * A structure that consists of a set of revoked associations that pending for role holder + * removal per each user. + * + * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo) + * @see #addToPendingRoleHolderRemoval(AssociationInfo) + * @see #removeFromPendingRoleHolderRemoval(AssociationInfo) + * @see #getPendingRoleHolderRemovalAssociationsForUser(int) + */ + @GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval") + private final PerUserAssociationSet mRevokedAssociationsPendingRoleHolderRemoval = + new PerUserAssociationSet(); + /** + * Contains uid-s of packages pending to be removed from the role holder list (after + * revocation of an association), which will happen one the package is no longer visible to the + * user. + * For quicker uid -> (userId, packageName) look-up this is not a {@code Set<Integer>} but + * a {@code Map<Integer, String>} which maps uid-s to packageName-s (userId-s can be derived + * from uid-s using {@link UserHandle#getUserId(int)}). + * + * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo) + * @see #addToPendingRoleHolderRemoval(AssociationInfo) + * @see #removeFromPendingRoleHolderRemoval(AssociationInfo) + */ + @GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval") + private final Map<Integer, String> mUidsPendingRoleHolderRemoval = new HashMap<>(); + + AssociationRevokeProcessor(@NonNull CompanionDeviceManagerService service, + @NonNull AssociationStoreImpl associationStore, + @NonNull PackageManagerInternal packageManager, + @NonNull CompanionDevicePresenceMonitor devicePresenceMonitor, + @NonNull CompanionApplicationController applicationController, + @NonNull SystemDataTransferRequestStore systemDataTransferRequestStore) { + mService = service; + mContext = service.getContext(); + mActivityManager = mContext.getSystemService(ActivityManager.class); + mAssociationStore = associationStore; + mPackageManagerInternal = packageManager; + mOnPackageVisibilityChangeListener = + new OnPackageVisibilityChangeListener(mActivityManager); + mDevicePresenceMonitor = devicePresenceMonitor; + mCompanionAppController = applicationController; + mSystemDataTransferRequestStore = systemDataTransferRequestStore; + } + + // TODO: also revoke notification access + void disassociateInternal(int associationId) { + final AssociationInfo association = mAssociationStore.getAssociationById(associationId); + final int userId = association.getUserId(); + final String packageName = association.getPackageName(); + final String deviceProfile = association.getDeviceProfile(); + + if (!maybeRemoveRoleHolderForAssociation(association)) { + // Need to remove the app from list of the role holders, but will have to do it later + // (the app is in foreground at the moment). + addToPendingRoleHolderRemoval(association); + } + + // Need to check if device still present now because CompanionDevicePresenceMonitor will + // remove current connected device after mAssociationStore.removeAssociation + final boolean wasPresent = mDevicePresenceMonitor.isDevicePresent(associationId); + + // Removing the association. + mAssociationStore.removeAssociation(associationId); + // Do not need to persistUserState since CompanionDeviceManagerService will get callback + // from #onAssociationChanged, and it will handle the persistUserState which including + // active and revoked association. + logRemoveAssociation(deviceProfile); + + // Remove all the system data transfer requests for the association. + mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId); + + if (!wasPresent || !association.isNotifyOnDeviceNearby()) return; + // The device was connected and the app was notified: check if we need to unbind the app + // now. + final boolean shouldStayBound = any( + mAssociationStore.getAssociationsForPackage(userId, packageName), + it -> it.isNotifyOnDeviceNearby() + && mDevicePresenceMonitor.isDevicePresent(it.getId())); + if (shouldStayBound) return; + mCompanionAppController.unbindCompanionApplication(userId, packageName); + } + + /** + * First, checks if the companion application should be removed from the list role holders when + * upon association's removal, i.e.: association's profile (matches the role) is not null, + * the application does not have other associations with the same profile, etc. + * + * <p> + * Then, if establishes that the application indeed has to be removed from the list of the role + * holders, checks if it could be done right now - + * {@link android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, java.util.concurrent.Executor, java.util.function.Consumer) RoleManager#removeRoleHolderAsUser()} + * will kill the application's process, which leads poor user experience if the application was + * in foreground when this happened, to avoid this CDMS delays invoking + * {@code RoleManager.removeRoleHolderAsUser()} until the app is no longer in foreground. + * + * @return {@code true} if the application does NOT need be removed from the list of the role + * holders OR if the application was successfully removed from the list of role holders. + * I.e.: from the role-management perspective the association is done with. + * {@code false} if the application needs to be removed from the list of role the role + * holders, BUT it CDMS would prefer to do it later. + * I.e.: application is in the foreground at the moment, but invoking + * {@code RoleManager.removeRoleHolderAsUser()} will kill the application's process, + * which would lead to the poor UX, hence need to try later. + */ + boolean maybeRemoveRoleHolderForAssociation(@NonNull AssociationInfo association) { + if (DEBUG) Log.d(TAG, "maybeRemoveRoleHolderForAssociation() association=" + association); + final String deviceProfile = association.getDeviceProfile(); + + if (deviceProfile == null) { + // No role was granted to for this association, there is nothing else we need to here. + return true; + } + // Do not need to remove the system role since it was pre-granted by the system. + if (deviceProfile.equals(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION)) { + return true; + } + + // Check if the applications is associated with another devices with the profile. If so, + // it should remain the role holder. + final int id = association.getId(); + final int userId = association.getUserId(); + final String packageName = association.getPackageName(); + final boolean roleStillInUse = any( + mAssociationStore.getAssociationsForPackage(userId, packageName), + it -> deviceProfile.equals(it.getDeviceProfile()) && id != it.getId()); + if (roleStillInUse) { + // Application should remain a role holder, there is nothing else we need to here. + return true; + } + + final int packageProcessImportance = getPackageProcessImportance(userId, packageName); + if (packageProcessImportance <= IMPORTANCE_VISIBLE) { + // Need to remove the app from the list of role holders, but the process is visible to + // the user at the moment, so we'll need to it later: log and return false. + Slog.i(TAG, "Cannot remove role holder for the removed association id=" + id + + " now - process is visible."); + return false; + } + + removeRoleHolderForAssociation(mContext, association); + return true; + } + + @SuppressLint("MissingPermission") + private int getPackageProcessImportance(@UserIdInt int userId, @NonNull String packageName) { + return Binder.withCleanCallingIdentity(() -> { + final int uid = + mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId); + return mActivityManager.getUidImportance(uid); + }); + } + + /** + * Set revoked flag for active association and add the revoked association and the uid into + * the caches. + * + * @see #mRevokedAssociationsPendingRoleHolderRemoval + * @see #mUidsPendingRoleHolderRemoval + * @see OnPackageVisibilityChangeListener + */ + void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) { + // First: set revoked flag + association = (new AssociationInfo.Builder(association)).setRevoked(true).build(); + final String packageName = association.getPackageName(); + final int userId = association.getUserId(); + final int uid = mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId); + // Second: add to the set. + synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { + mRevokedAssociationsPendingRoleHolderRemoval.forUser(association.getUserId()) + .add(association); + if (!mUidsPendingRoleHolderRemoval.containsKey(uid)) { + mUidsPendingRoleHolderRemoval.put(uid, packageName); + + if (mUidsPendingRoleHolderRemoval.size() == 1) { + // Just added first uid: start the listener + mOnPackageVisibilityChangeListener.startListening(); + } + } + } + } + + /** + * Remove the revoked association from the cache and also remove the uid from the map if + * there are other associations with the same package still pending for role holder removal. + * + * @see #mRevokedAssociationsPendingRoleHolderRemoval + * @see #mUidsPendingRoleHolderRemoval + * @see OnPackageVisibilityChangeListener + */ + private void removeFromPendingRoleHolderRemoval(@NonNull AssociationInfo association) { + final String packageName = association.getPackageName(); + final int userId = association.getUserId(); + final int uid = mPackageManagerInternal.getPackageUid(packageName, /* flags */ 0, userId); + + synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { + mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId) + .remove(association); + + final boolean shouldKeepUidForRemoval = any( + getPendingRoleHolderRemovalAssociationsForUser(userId), + ai -> packageName.equals(ai.getPackageName())); + // Do not remove the uid from the map since other associations with + // the same packageName still pending for role holder removal. + if (!shouldKeepUidForRemoval) { + mUidsPendingRoleHolderRemoval.remove(uid); + } + + if (mUidsPendingRoleHolderRemoval.isEmpty()) { + // The set is empty now - can "turn off" the listener. + mOnPackageVisibilityChangeListener.stopListening(); + } + } + } + + /** + * @return a copy of the revoked associations set (safeguarding against + * {@code ConcurrentModificationException}-s). + */ + @NonNull Set<AssociationInfo> getPendingRoleHolderRemovalAssociationsForUser( + @UserIdInt int userId) { + synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { + // Return a copy. + return new ArraySet<>(mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId)); + } + } + + private String getPackageNameByUid(int uid) { + synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { + return mUidsPendingRoleHolderRemoval.get(uid); + } + } + + /** + * An OnUidImportanceListener class which watches the importance of the packages. + * In this class, we ONLY interested in the importance of the running process is greater than + * {@link ActivityManager.RunningAppProcessInfo#IMPORTANCE_VISIBLE} for the uids have been added + * into the {@link #mUidsPendingRoleHolderRemoval}. Lastly remove the role holder for the + * revoked associations for the same packages. + * + * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo) + * @see #removeFromPendingRoleHolderRemoval(AssociationInfo) + * @see #getPendingRoleHolderRemovalAssociationsForUser(int) + */ + private class OnPackageVisibilityChangeListener implements + ActivityManager.OnUidImportanceListener { + final @NonNull ActivityManager mAm; + + OnPackageVisibilityChangeListener(@NonNull ActivityManager am) { + this.mAm = am; + } + + @SuppressLint("MissingPermission") + void startListening() { + Binder.withCleanCallingIdentity( + () -> mAm.addOnUidImportanceListener( + /* listener */ OnPackageVisibilityChangeListener.this, + ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE)); + } + + @SuppressLint("MissingPermission") + void stopListening() { + Binder.withCleanCallingIdentity( + () -> mAm.removeOnUidImportanceListener( + /* listener */ OnPackageVisibilityChangeListener.this)); + } + + @Override + public void onUidImportance(int uid, int importance) { + if (importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { + // The lower the importance value the more "important" the process is. + // We are only interested when the process ceases to be visible. + return; + } + + final String packageName = getPackageNameByUid(uid); + if (packageName == null) { + // Not interested in this uid. + return; + } + + final int userId = UserHandle.getUserId(uid); + + boolean needToPersistStateForUser = false; + + for (AssociationInfo association : + getPendingRoleHolderRemovalAssociationsForUser(userId)) { + if (!packageName.equals(association.getPackageName())) continue; + + if (!maybeRemoveRoleHolderForAssociation(association)) { + // Did not remove the role holder, will have to try again later. + continue; + } + + removeFromPendingRoleHolderRemoval(association); + needToPersistStateForUser = true; + } + + if (needToPersistStateForUser) { + mService.postPersistUserState(userId); + } + } + } +} diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index ba1f51baa624..09c77939eb7b 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -22,7 +22,6 @@ import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES; import static android.Manifest.permission.MANAGE_COMPANION_DEVICES; import static android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE; import static android.Manifest.permission.USE_COMPANION_TRANSPORTS; -import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION; import static android.companion.DevicePresenceEvent.EVENT_BLE_APPEARED; import static android.companion.DevicePresenceEvent.EVENT_BLE_DISAPPEARED; @@ -39,7 +38,6 @@ import static com.android.internal.util.CollectionUtils.any; import static com.android.internal.util.Preconditions.checkState; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED; -import static com.android.server.companion.MetricUtils.logRemoveAssociation; import static com.android.server.companion.PackageUtils.isRestrictedSettingsAllowed; import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature; import static com.android.server.companion.PackageUtils.getPackageInfo; @@ -49,7 +47,6 @@ import static com.android.server.companion.PermissionsUtils.enforceCallerCanObse import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr; import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId; import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks; -import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.DAYS; @@ -61,7 +58,6 @@ import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.ActivityManager; -import android.app.ActivityManager.RunningAppProcessInfo; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.NotificationManager; @@ -149,7 +145,7 @@ public class CompanionDeviceManagerService extends SystemService { static final String TAG = "CDM_CompanionDeviceManagerService"; static final boolean DEBUG = false; - /** Range of Association IDs allocated for a user.*/ + /** Range of Association IDs allocated for a user. */ private static final int ASSOCIATIONS_IDS_PER_USER_RANGE = 100000; private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min @@ -162,8 +158,6 @@ public class CompanionDeviceManagerService extends SystemService { private static final int MAX_CN_LENGTH = 500; private final ActivityManager mActivityManager; - private final OnPackageVisibilityChangeListener mOnPackageVisibilityChangeListener; - private PersistentDataStore mPersistentStore; private final PersistUserStateHandler mUserPersistenceHandler; @@ -175,6 +169,7 @@ public class CompanionDeviceManagerService extends SystemService { private CompanionDevicePresenceMonitor mDevicePresenceMonitor; private CompanionApplicationController mCompanionAppController; private CompanionTransportManager mTransportManager; + private AssociationRevokeProcessor mAssociationRevokeProcessor; private final ActivityTaskManagerInternal mAtmInternal; private final ActivityManagerInternal mAmInternal; @@ -193,33 +188,6 @@ public class CompanionDeviceManagerService extends SystemService { @GuardedBy("mPreviouslyUsedIds") private final SparseArray<Map<String, Set<Integer>>> mPreviouslyUsedIds = new SparseArray<>(); - /** - * A structure that consists of a set of revoked associations that pending for role holder - * removal per each user. - * - * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo) - * @see #addToPendingRoleHolderRemoval(AssociationInfo) - * @see #removeFromPendingRoleHolderRemoval(AssociationInfo) - * @see #getPendingRoleHolderRemovalAssociationsForUser(int) - */ - @GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval") - private final PerUserAssociationSet mRevokedAssociationsPendingRoleHolderRemoval = - new PerUserAssociationSet(); - /** - * Contains uid-s of packages pending to be removed from the role holder list (after - * revocation of an association), which will happen one the package is no longer visible to the - * user. - * For quicker uid -> (userId, packageName) look-up this is not a {@code Set<Integer>} but - * a {@code Map<Integer, String>} which maps uid-s to packageName-s (userId-s can be derived - * from uid-s using {@link UserHandle#getUserId(int)}). - * - * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo) - * @see #addToPendingRoleHolderRemoval(AssociationInfo) - * @see #removeFromPendingRoleHolderRemoval(AssociationInfo) - */ - @GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval") - private final Map<Integer, String> mUidsPendingRoleHolderRemoval = new HashMap<>(); - private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners = new RemoteCallbackList<>(); @@ -243,8 +211,6 @@ public class CompanionDeviceManagerService extends SystemService { mAssociationStore = new AssociationStoreImpl(); mSystemDataTransferRequestStore = new SystemDataTransferRequestStore(); - mOnPackageVisibilityChangeListener = - new OnPackageVisibilityChangeListener(mActivityManager); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mObservableUuidStore = new ObservableUuidStore(); } @@ -276,6 +242,9 @@ public class CompanionDeviceManagerService extends SystemService { mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mPackageManagerInternal, mAssociationStore, mSystemDataTransferRequestStore, mTransportManager); + mAssociationRevokeProcessor = new AssociationRevokeProcessor(this, mAssociationStore, + mPackageManagerInternal, mDevicePresenceMonitor, mCompanionAppController, + mSystemDataTransferRequestStore); // TODO(b/279663946): move context sync to a dedicated system service mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager); @@ -307,12 +276,13 @@ public class CompanionDeviceManagerService extends SystemService { mBackupRestoreProcessor.addToPendingAppInstall(association); } else if (!association.isRevoked()) { activeAssociations.add(association); - } else if (maybeRemoveRoleHolderForAssociation(association)) { + } else if (mAssociationRevokeProcessor.maybeRemoveRoleHolderForAssociation( + association)) { // Nothing more to do here, but we'll need to persist all the associations to the // disk afterwards. usersToPersistStateFor.add(association.getUserId()); } else { - addToPendingRoleHolderRemoval(association); + mAssociationRevokeProcessor.addToPendingRoleHolderRemoval(association); } } @@ -374,7 +344,7 @@ public class CompanionDeviceManagerService extends SystemService { final List<ParcelUuid> deviceUuids = ArrayUtils.isEmpty(bluetoothDeviceUuids) ? Collections.emptyList() : Arrays.asList(bluetoothDeviceUuids); - for (AssociationInfo ai: + for (AssociationInfo ai : mAssociationStore.getAssociationsByAddress(bluetoothDevice.getAddress())) { Slog.i(TAG, "onUserUnlocked, device id( " + ai.getId() + " ) is connected"); mDevicePresenceMonitor.onBluetoothCompanionDeviceConnected(ai.getId()); @@ -495,7 +465,7 @@ public class CompanionDeviceManagerService extends SystemService { final String packageName = uuid.getPackageName(); final int userId = uuid.getUserId(); - switch(event) { + switch (event) { case EVENT_BT_CONNECTED: if (!mCompanionAppController.isCompanionApplicationBound(userId, packageName)) { mCompanionAppController.bindCompanionApplication( @@ -544,8 +514,8 @@ public class CompanionDeviceManagerService extends SystemService { /** * @return whether the package should be bound (i.e. at least one of the devices associated with - * the package is currently present OR the UUID to be observed by this package is - * currently present). + * the package is currently present OR the UUID to be observed by this package is + * currently present). */ private boolean shouldBindPackage(@UserIdInt int userId, @NonNull String packageName) { final List<AssociationInfo> packageAssociations = @@ -599,7 +569,8 @@ public class CompanionDeviceManagerService extends SystemService { allAssociations = new ArrayList<>( mAssociationStore.getAssociationsForUser(userId)); // ... and add the revoked (removed) association, that are yet to be permanently removed. - allAssociations.addAll(getPendingRoleHolderRemovalAssociationsForUser(userId)); + allAssociations.addAll( + mAssociationRevokeProcessor.getPendingRoleHolderRemovalAssociationsForUser(userId)); // ... and add the restored associations that are pending missing package installation. allAssociations.addAll(mBackupRestoreProcessor .getAssociationsPendingAppInstallForUser(userId)); @@ -654,7 +625,7 @@ public class CompanionDeviceManagerService extends SystemService { } // Clear role holders for (AssociationInfo association : associationsForPackage) { - maybeRemoveRoleHolderForAssociation(association); + mAssociationRevokeProcessor.maybeRemoveRoleHolderForAssociation(association); } // Clear the uuids to be observed. for (ObservableUuid uuid : uuidsTobeObserved) { @@ -712,7 +683,7 @@ public class CompanionDeviceManagerService extends SystemService { final int id = association.getId(); Slog.i(TAG, "Removing inactive self-managed association id=" + id); - disassociateInternal(id); + mAssociationRevokeProcessor.disassociateInternal(id); } } @@ -857,7 +828,7 @@ public class CompanionDeviceManagerService extends SystemService { final AssociationInfo association = getAssociationWithCallerChecks(userId, packageName, deviceMacAddress); - disassociateInternal(association.getId()); + mAssociationRevokeProcessor.disassociateInternal(association.getId()); } @Override @@ -866,7 +837,7 @@ public class CompanionDeviceManagerService extends SystemService { final AssociationInfo association = getAssociationWithCallerChecks(associationId); - disassociateInternal(association.getId()); + mAssociationRevokeProcessor.disassociateInternal(association.getId()); } @Override @@ -902,9 +873,9 @@ public class CompanionDeviceManagerService extends SystemService { } /** - * @deprecated Use - * {@link NotificationManager#isNotificationListenerAccessGranted(ComponentName)} instead. - */ + * @deprecated Use + * {@link NotificationManager#isNotificationListenerAccessGranted(ComponentName)} instead. + */ @Deprecated @Override public boolean hasNotificationAccess(ComponentName component) throws RemoteException { @@ -1300,7 +1271,7 @@ public class CompanionDeviceManagerService extends SystemService { return new CompanionDeviceShellCommand(CompanionDeviceManagerService.this, mAssociationStore, mDevicePresenceMonitor, mTransportManager, mSystemDataTransferProcessor, mAssociationRequestsProcessor, - mBackupRestoreProcessor) + mBackupRestoreProcessor, mAssociationRevokeProcessor) .exec(this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args); } @@ -1381,7 +1352,7 @@ public class CompanionDeviceManagerService extends SystemService { // another association by the time when it is activated from the package installation. final Set<AssociationInfo> pendingAssociations = mBackupRestoreProcessor .getAssociationsPendingAppInstallForUser(userId); - for (AssociationInfo it: pendingAssociations) { + for (AssociationInfo it : pendingAssociations) { usedIds.put(it.getId(), true); } @@ -1407,198 +1378,6 @@ public class CompanionDeviceManagerService extends SystemService { } } - // TODO: also revoke notification access - void disassociateInternal(int associationId) { - final AssociationInfo association = mAssociationStore.getAssociationById(associationId); - final int userId = association.getUserId(); - final String packageName = association.getPackageName(); - final String deviceProfile = association.getDeviceProfile(); - - if (!maybeRemoveRoleHolderForAssociation(association)) { - // Need to remove the app from list of the role holders, but will have to do it later - // (the app is in foreground at the moment). - addToPendingRoleHolderRemoval(association); - } - - // Need to check if device still present now because CompanionDevicePresenceMonitor will - // remove current connected device after mAssociationStore.removeAssociation - final boolean wasPresent = mDevicePresenceMonitor.isDevicePresent(associationId); - - // Removing the association. - mAssociationStore.removeAssociation(associationId); - // Do not need to persistUserState since CompanionDeviceManagerService will get callback - // from #onAssociationChanged, and it will handle the persistUserState which including - // active and revoked association. - logRemoveAssociation(deviceProfile); - - // Remove all the system data transfer requests for the association. - mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId); - - if (!wasPresent || !association.isNotifyOnDeviceNearby()) return; - // The device was connected and the app was notified: check if we need to unbind the app - // now. - final boolean shouldStayBound = any( - mAssociationStore.getAssociationsForPackage(userId, packageName), - it -> it.isNotifyOnDeviceNearby() - && mDevicePresenceMonitor.isDevicePresent(it.getId())); - if (shouldStayBound) return; - mCompanionAppController.unbindCompanionApplication(userId, packageName); - } - - /** - * First, checks if the companion application should be removed from the list role holders when - * upon association's removal, i.e.: association's profile (matches the role) is not null, - * the application does not have other associations with the same profile, etc. - * - * <p> - * Then, if establishes that the application indeed has to be removed from the list of the role - * holders, checks if it could be done right now - - * {@link android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, java.util.concurrent.Executor, java.util.function.Consumer) RoleManager#removeRoleHolderAsUser()} - * will kill the application's process, which leads poor user experience if the application was - * in foreground when this happened, to avoid this CDMS delays invoking - * {@code RoleManager.removeRoleHolderAsUser()} until the app is no longer in foreground. - * - * @return {@code true} if the application does NOT need be removed from the list of the role - * holders OR if the application was successfully removed from the list of role holders. - * I.e.: from the role-management perspective the association is done with. - * {@code false} if the application needs to be removed from the list of role the role - * holders, BUT it CDMS would prefer to do it later. - * I.e.: application is in the foreground at the moment, but invoking - * {@code RoleManager.removeRoleHolderAsUser()} will kill the application's process, - * which would lead to the poor UX, hence need to try later. - */ - - private boolean maybeRemoveRoleHolderForAssociation(@NonNull AssociationInfo association) { - if (DEBUG) Log.d(TAG, "maybeRemoveRoleHolderForAssociation() association=" + association); - - final String deviceProfile = association.getDeviceProfile(); - if (deviceProfile == null) { - // No role was granted to for this association, there is nothing else we need to here. - return true; - } - // Do not need to remove the system role since it was pre-granted by the system. - if (deviceProfile.equals(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION)) { - return true; - } - - // Check if the applications is associated with another devices with the profile. If so, - // it should remain the role holder. - final int id = association.getId(); - final int userId = association.getUserId(); - final String packageName = association.getPackageName(); - final boolean roleStillInUse = any( - mAssociationStore.getAssociationsForPackage(userId, packageName), - it -> deviceProfile.equals(it.getDeviceProfile()) && id != it.getId()); - if (roleStillInUse) { - // Application should remain a role holder, there is nothing else we need to here. - return true; - } - - final int packageProcessImportance = getPackageProcessImportance(userId, packageName); - if (packageProcessImportance <= IMPORTANCE_VISIBLE) { - // Need to remove the app from the list of role holders, but the process is visible to - // the user at the moment, so we'll need to it later: log and return false. - Slog.i(TAG, "Cannot remove role holder for the removed association id=" + id - + " now - process is visible."); - return false; - } - - removeRoleHolderForAssociation(getContext(), association); - return true; - } - - private int getPackageProcessImportance(@UserIdInt int userId, @NonNull String packageName) { - return Binder.withCleanCallingIdentity(() -> { - final int uid = - mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId); - return mActivityManager.getUidImportance(uid); - }); - } - - /** - * Set revoked flag for active association and add the revoked association and the uid into - * the caches. - * - * @see #mRevokedAssociationsPendingRoleHolderRemoval - * @see #mUidsPendingRoleHolderRemoval - * @see OnPackageVisibilityChangeListener - */ - private void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) { - // First: set revoked flag. - association = (new AssociationInfo.Builder(association)) - .setRevoked(true) - .build(); - - final String packageName = association.getPackageName(); - final int userId = association.getUserId(); - final int uid = mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId); - - // Second: add to the set. - synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { - mRevokedAssociationsPendingRoleHolderRemoval.forUser(association.getUserId()) - .add(association); - if (!mUidsPendingRoleHolderRemoval.containsKey(uid)) { - mUidsPendingRoleHolderRemoval.put(uid, packageName); - - if (mUidsPendingRoleHolderRemoval.size() == 1) { - // Just added first uid: start the listener - mOnPackageVisibilityChangeListener.startListening(); - } - } - } - } - - /** - * Remove the revoked association from the cache and also remove the uid from the map if - * there are other associations with the same package still pending for role holder removal. - * - * @see #mRevokedAssociationsPendingRoleHolderRemoval - * @see #mUidsPendingRoleHolderRemoval - * @see OnPackageVisibilityChangeListener - */ - private void removeFromPendingRoleHolderRemoval(@NonNull AssociationInfo association) { - final String packageName = association.getPackageName(); - final int userId = association.getUserId(); - final int uid = mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId); - - synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { - mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId) - .remove(association); - - final boolean shouldKeepUidForRemoval = any( - getPendingRoleHolderRemovalAssociationsForUser(userId), - ai -> packageName.equals(ai.getPackageName())); - // Do not remove the uid from the map since other associations with - // the same packageName still pending for role holder removal. - if (!shouldKeepUidForRemoval) { - mUidsPendingRoleHolderRemoval.remove(uid); - } - - if (mUidsPendingRoleHolderRemoval.isEmpty()) { - // The set is empty now - can "turn off" the listener. - mOnPackageVisibilityChangeListener.stopListening(); - } - } - } - - /** - * @return a copy of the revoked associations set (safeguarding against - * {@code ConcurrentModificationException}-s). - */ - private @NonNull Set<AssociationInfo> getPendingRoleHolderRemovalAssociationsForUser( - @UserIdInt int userId) { - synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { - // Return a copy. - return new ArraySet<>(mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId)); - } - } - - private String getPackageNameByUid(int uid) { - synchronized (mRevokedAssociationsPendingRoleHolderRemoval) { - return mUidsPendingRoleHolderRemoval.get(uid); - } - } - void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) { final PackageInfo packageInfo = getPackageInfo(getContext(), association.getUserId(), association.getPackageName()); @@ -1704,11 +1483,11 @@ public class CompanionDeviceManagerService extends SystemService { private final AssociationStore.OnChangeListener mAssociationStoreChangeListener = new AssociationStore.OnChangeListener() { - @Override - public void onAssociationChanged(int changeType, AssociationInfo association) { - onAssociationChangedInternal(changeType, association); - } - }; + @Override + public void onAssociationChanged(int changeType, AssociationInfo association) { + onAssociationChangedInternal(changeType, association); + } + }; private final CompanionDevicePresenceMonitor.Callback mDevicePresenceCallback = new CompanionDevicePresenceMonitor.Callback() { @@ -1731,7 +1510,7 @@ public class CompanionDeviceManagerService extends SystemService { public void onDevicePresenceEventByUuid(ObservableUuid uuid, int event) { onDevicePresenceEventByUuidInternal(uuid, event); } - }; + }; private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override @@ -1887,73 +1666,8 @@ public class CompanionDeviceManagerService extends SystemService { } } - /** - * An OnUidImportanceListener class which watches the importance of the packages. - * In this class, we ONLY interested in the importance of the running process is greater than - * {@link RunningAppProcessInfo.IMPORTANCE_VISIBLE} for the uids have been added into the - * {@link mUidsPendingRoleHolderRemoval}. Lastly remove the role holder for the revoked - * associations for the same packages. - * - * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo) - * @see #removeFromPendingRoleHolderRemoval(AssociationInfo) - * @see #getPendingRoleHolderRemovalAssociationsForUser(int) - */ - private class OnPackageVisibilityChangeListener implements - ActivityManager.OnUidImportanceListener { - final @NonNull ActivityManager mAm; - - OnPackageVisibilityChangeListener(@NonNull ActivityManager am) { - this.mAm = am; - } - - void startListening() { - Binder.withCleanCallingIdentity( - () -> mAm.addOnUidImportanceListener( - /* listener */ OnPackageVisibilityChangeListener.this, - RunningAppProcessInfo.IMPORTANCE_VISIBLE)); - } - - void stopListening() { - Binder.withCleanCallingIdentity( - () -> mAm.removeOnUidImportanceListener( - /* listener */ OnPackageVisibilityChangeListener.this)); - } - - @Override - public void onUidImportance(int uid, int importance) { - if (importance <= RunningAppProcessInfo.IMPORTANCE_VISIBLE) { - // The lower the importance value the more "important" the process is. - // We are only interested when the process ceases to be visible. - return; - } - - final String packageName = getPackageNameByUid(uid); - if (packageName == null) { - // Not interested in this uid. - return; - } - - final int userId = UserHandle.getUserId(uid); - - boolean needToPersistStateForUser = false; - - for (AssociationInfo association : - getPendingRoleHolderRemovalAssociationsForUser(userId)) { - if (!packageName.equals(association.getPackageName())) continue; - - if (!maybeRemoveRoleHolderForAssociation(association)) { - // Did not remove the role holder, will have to try again later. - continue; - } - - removeFromPendingRoleHolderRemoval(association); - needToPersistStateForUser = true; - } - - if (needToPersistStateForUser) { - mUserPersistenceHandler.postPersistUserState(userId); - } - } + void postPersistUserState(@UserIdInt int userId) { + mUserPersistenceHandler.postPersistUserState(userId); } static class PerUserAssociationSet extends PerUser<Set<AssociationInfo>> { diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index 5663434e2b6d..de4f2b60170f 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -45,6 +45,7 @@ class CompanionDeviceShellCommand extends ShellCommand { private static final String TAG = "CDM_CompanionDeviceShellCommand"; private final CompanionDeviceManagerService mService; + private final AssociationRevokeProcessor mRevokeProcessor; private final AssociationStoreImpl mAssociationStore; private final CompanionDevicePresenceMonitor mDevicePresenceMonitor; private final CompanionTransportManager mTransportManager; @@ -59,7 +60,8 @@ class CompanionDeviceShellCommand extends ShellCommand { CompanionTransportManager transportManager, SystemDataTransferProcessor systemDataTransferProcessor, AssociationRequestsProcessor associationRequestsProcessor, - BackupRestoreProcessor backupRestoreProcessor) { + BackupRestoreProcessor backupRestoreProcessor, + AssociationRevokeProcessor revokeProcessor) { mService = service; mAssociationStore = associationStore; mDevicePresenceMonitor = devicePresenceMonitor; @@ -67,6 +69,7 @@ class CompanionDeviceShellCommand extends ShellCommand { mSystemDataTransferProcessor = systemDataTransferProcessor; mAssociationRequestsProcessor = associationRequestsProcessor; mBackupRestoreProcessor = backupRestoreProcessor; + mRevokeProcessor = revokeProcessor; } @Override @@ -126,7 +129,7 @@ class CompanionDeviceShellCommand extends ShellCommand { final AssociationInfo association = mService.getAssociationWithCallerChecks(userId, packageName, address); if (association != null) { - mService.disassociateInternal(association.getId()); + mRevokeProcessor.disassociateInternal(association.getId()); } } break; @@ -138,7 +141,7 @@ class CompanionDeviceShellCommand extends ShellCommand { mAssociationStore.getAssociationsForPackage(userId, packageName); for (AssociationInfo association : userAssociations) { if (sanitizeWithCallerChecks(mService.getContext(), association) != null) { - mService.disassociateInternal(association.getId()); + mRevokeProcessor.disassociateInternal(association.getId()); } } } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 082776ad6085..fb4976d3cef2 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -16,6 +16,7 @@ package com.android.server.display; +import static com.android.server.display.BrightnessMappingStrategy.INVALID_LUX; import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString; import android.annotation.IntDef; @@ -202,7 +203,7 @@ public class AutomaticBrightnessController { private float mScreenBrighteningThreshold; private float mScreenDarkeningThreshold; // The most recent light sample. - private float mLastObservedLux; + private float mLastObservedLux = INVALID_LUX; // The time of the most light recent sample. private long mLastObservedLuxTime; @@ -403,8 +404,8 @@ public class AutomaticBrightnessController { brightnessEvent.setFlags(brightnessEvent.getFlags() | (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0) | (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE - ? BrightnessEvent.FLAG_DOZE_SCALE : 0) - | (isInIdleMode() ? BrightnessEvent.FLAG_IDLE_CURVE : 0)); + ? BrightnessEvent.FLAG_DOZE_SCALE : 0)); + brightnessEvent.setAutoBrightnessMode(getMode()); } if (!mAmbientLuxValid) { @@ -420,6 +421,35 @@ public class AutomaticBrightnessController { return mRawScreenAutoBrightness; } + /** + * Get the automatic screen brightness based on the last observed lux reading. Used e.g. when + * entering doze - we disable the light sensor, invalidate the lux, but we still need to set + * the initial brightness in doze mode. + */ + public float getAutomaticScreenBrightnessBasedOnLastObservedLux( + BrightnessEvent brightnessEvent) { + if (mLastObservedLux == INVALID_LUX) { + return PowerManager.BRIGHTNESS_INVALID_FLOAT; + } + + float brightness = mCurrentBrightnessMapper.getBrightness(mLastObservedLux, + mForegroundAppPackageName, mForegroundAppCategory); + if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) { + brightness *= mDozeScaleFactor; + } + + if (brightnessEvent != null) { + brightnessEvent.setLux(mLastObservedLux); + brightnessEvent.setRecommendedBrightness(brightness); + brightnessEvent.setFlags(brightnessEvent.getFlags() + | (mLastObservedLux == INVALID_LUX ? BrightnessEvent.FLAG_INVALID_LUX : 0) + | (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE + ? BrightnessEvent.FLAG_DOZE_SCALE : 0)); + brightnessEvent.setAutoBrightnessMode(getMode()); + } + return brightness; + } + public boolean hasValidAmbientLux() { return mAmbientLuxValid; } @@ -539,7 +569,7 @@ public class AutomaticBrightnessController { } private boolean setScreenBrightnessByUser(float lux, float brightness) { - if (lux == BrightnessMappingStrategy.INVALID_LUX || Float.isNaN(brightness)) { + if (lux == INVALID_LUX || Float.isNaN(brightness)) { return false; } mCurrentBrightnessMapper.addUserDataPoint(lux, brightness); @@ -564,6 +594,15 @@ public class AutomaticBrightnessController { return false; } + /** + * @return The auto-brightness mode of the current mapping strategy. Different modes use + * different brightness curves. + */ + @AutomaticBrightnessController.AutomaticBrightnessMode + public int getMode() { + return mCurrentBrightnessMapper.getMode(); + } + public boolean isInIdleMode() { return mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_IDLE; } @@ -1236,12 +1275,12 @@ public class AutomaticBrightnessController { // light. // The anchor determines what were the light levels when the user has set their preference, // and we use a relative threshold to determine when to revert to the OEM curve. - private float mAnchor = BrightnessMappingStrategy.INVALID_LUX; + private float mAnchor = INVALID_LUX; private float mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; private boolean mIsValid = false; private void reset() { - mAnchor = BrightnessMappingStrategy.INVALID_LUX; + mAnchor = INVALID_LUX; mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; mIsValid = false; } @@ -1265,7 +1304,7 @@ public class AutomaticBrightnessController { private boolean maybeReset(float currentLux) { // If the short term model was invalidated and the change is drastic enough, reset it. // Otherwise, we revalidate it. - if (!mIsValid && mAnchor != BrightnessMappingStrategy.INVALID_LUX) { + if (!mIsValid && mAnchor != INVALID_LUX) { if (mCurrentBrightnessMapper.shouldResetShortTermModel(currentLux, mAnchor)) { resetShortTermModel(); } else { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 3965d55b0c28..dbe85ea0fa04 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; + import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; @@ -37,7 +39,6 @@ import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.BrightnessInfo; -import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession; import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; @@ -1378,7 +1379,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Switch to doze auto-brightness mode if needed if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null && !mAutomaticBrightnessController.isInIdleMode()) { - setAutomaticScreenBrightnessMode(Display.isDozeState(state) + mAutomaticBrightnessController.switchMode(mPowerRequest.policy == POLICY_DOZE ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT); } @@ -1466,6 +1467,22 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); } + // If there's an offload session and auto-brightness is on, we need to set the initial doze + // brightness using the doze auto-brightness curve before the offload session starts + // controlling the brightness. + if (Float.isNaN(brightnessState) && mFlags.areAutoBrightnessModesEnabled() + && mFlags.isDisplayOffloadEnabled() + && mPowerRequest.policy == POLICY_DOZE + && mDisplayOffloadSession != null + && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) { + rawBrightnessState = mAutomaticBrightnessController + .getAutomaticScreenBrightnessBasedOnLastObservedLux(mTempBrightnessEvent); + if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) { + brightnessState = clampScreenBrightness(rawBrightnessState); + mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL); + } + } + // Use default brightness when dozing unless overridden. if ((Float.isNaN(brightnessState)) && Display.isDozeState(state)) { @@ -1618,7 +1635,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // if doze or suspend state is requested, we want to finish brightnes animation fast // to allow state animation to start - if (mPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE + if (mPowerRequest.policy == POLICY_DOZE && (mPowerRequest.dozeScreenState == Display.STATE_UNKNOWN // dozing || mPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND || mPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND)) { @@ -1706,6 +1723,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mTempBrightnessEvent.setTime(System.currentTimeMillis()); mTempBrightnessEvent.setBrightness(brightnessState); mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId); + mTempBrightnessEvent.setDisplayState(state); mTempBrightnessEvent.setReason(mBrightnessReason); mTempBrightnessEvent.setHbmMax(mBrightnessRangeController.getCurrentBrightnessMax()); mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode()); @@ -2879,7 +2897,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call (flags & BrightnessEvent.FLAG_INVALID_LUX) > 0, (flags & BrightnessEvent.FLAG_DOZE_SCALE) > 0, (flags & BrightnessEvent.FLAG_USER_SET) > 0, - (flags & BrightnessEvent.FLAG_IDLE_CURVE) > 0, + event.getAutoBrightnessMode() == AUTO_BRIGHTNESS_MODE_IDLE, (flags & BrightnessEvent.FLAG_LOW_POWER_MODE) > 0); } } diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java index d4d1bae78a33..5423b74711a2 100644 --- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java +++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java @@ -16,11 +16,16 @@ package com.android.server.display.brightness; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString; + import android.hardware.display.BrightnessInfo; import android.os.PowerManager; import android.os.SystemClock; +import android.view.Display; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.AutomaticBrightnessController; import java.text.SimpleDateFormat; import java.util.Date; @@ -33,7 +38,6 @@ public final class BrightnessEvent { public static final int FLAG_INVALID_LUX = 0x2; public static final int FLAG_DOZE_SCALE = 0x4; public static final int FLAG_USER_SET = 0x8; - public static final int FLAG_IDLE_CURVE = 0x10; public static final int FLAG_LOW_POWER_MODE = 0x20; private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); @@ -41,6 +45,7 @@ public final class BrightnessEvent { private BrightnessReason mReason = new BrightnessReason(); private int mDisplayId; private String mPhysicalDisplayId; + private int mDisplayState; private long mTime; private float mLux; private float mPreThresholdLux; @@ -58,6 +63,8 @@ public final class BrightnessEvent { private int mAdjustmentFlags; private boolean mAutomaticBrightnessEnabled; private String mDisplayBrightnessStrategyName; + @AutomaticBrightnessController.AutomaticBrightnessMode + private int mAutoBrightnessMode; public BrightnessEvent(BrightnessEvent that) { copyFrom(that); @@ -77,6 +84,7 @@ public final class BrightnessEvent { mReason.set(that.getReason()); mDisplayId = that.getDisplayId(); mPhysicalDisplayId = that.getPhysicalDisplayId(); + mDisplayState = that.mDisplayState; mTime = that.getTime(); // Lux values mLux = that.getLux(); @@ -98,6 +106,7 @@ public final class BrightnessEvent { // Auto-brightness setting mAutomaticBrightnessEnabled = that.isAutomaticBrightnessEnabled(); mDisplayBrightnessStrategyName = that.getDisplayBrightnessStrategyName(); + mAutoBrightnessMode = that.mAutoBrightnessMode; } /** @@ -107,6 +116,7 @@ public final class BrightnessEvent { mReason = new BrightnessReason(); mTime = SystemClock.uptimeMillis(); mPhysicalDisplayId = ""; + mDisplayState = Display.STATE_UNKNOWN; // Lux values mLux = 0; mPreThresholdLux = 0; @@ -127,6 +137,7 @@ public final class BrightnessEvent { // Auto-brightness setting mAutomaticBrightnessEnabled = true; mDisplayBrightnessStrategyName = ""; + mAutoBrightnessMode = AUTO_BRIGHTNESS_MODE_DEFAULT; } /** @@ -143,6 +154,7 @@ public final class BrightnessEvent { return mReason.equals(that.mReason) && mDisplayId == that.mDisplayId && mPhysicalDisplayId.equals(that.mPhysicalDisplayId) + && mDisplayState == that.mDisplayState && Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux) && Float.floatToRawIntBits(mPreThresholdLux) == Float.floatToRawIntBits(that.mPreThresholdLux) @@ -163,7 +175,8 @@ public final class BrightnessEvent { && mFlags == that.mFlags && mAdjustmentFlags == that.mAdjustmentFlags && mAutomaticBrightnessEnabled == that.mAutomaticBrightnessEnabled - && mDisplayBrightnessStrategyName.equals(that.mDisplayBrightnessStrategyName); + && mDisplayBrightnessStrategyName.equals(that.mDisplayBrightnessStrategyName) + && mAutoBrightnessMode == that.mAutoBrightnessMode; } /** @@ -177,6 +190,7 @@ public final class BrightnessEvent { + "BrightnessEvent: " + "disp=" + mDisplayId + ", physDisp=" + mPhysicalDisplayId + + ", displayState=" + Display.stateToString(mDisplayState) + ", brt=" + mBrightness + ((mFlags & FLAG_USER_SET) != 0 ? "(user_set)" : "") + ", initBrt=" + mInitialBrightness + ", rcmdBrt=" + mRecommendedBrightness @@ -192,7 +206,8 @@ public final class BrightnessEvent { + ", flags=" + flagsToString() + ", reason=" + mReason.toString(mAdjustmentFlags) + ", autoBrightness=" + mAutomaticBrightnessEnabled - + ", strategy=" + mDisplayBrightnessStrategyName; + + ", strategy=" + mDisplayBrightnessStrategyName + + ", autoBrightnessMode=" + autoBrightnessModeToString(mAutoBrightnessMode); } @Override @@ -232,6 +247,10 @@ public final class BrightnessEvent { this.mPhysicalDisplayId = mPhysicalDisplayId; } + public void setDisplayState(int state) { + mDisplayState = state; + } + public float getLux() { return mLux; } @@ -374,6 +393,16 @@ public final class BrightnessEvent { this.mAutomaticBrightnessEnabled = mAutomaticBrightnessEnabled; } + @AutomaticBrightnessController.AutomaticBrightnessMode + public int getAutoBrightnessMode() { + return mAutoBrightnessMode; + } + + public void setAutoBrightnessMode( + @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { + mAutoBrightnessMode = mode; + } + /** * A utility to stringify flags from a BrightnessEvent * @return Stringified flags from BrightnessEvent @@ -384,7 +413,6 @@ public final class BrightnessEvent { + ((mFlags & FLAG_RBC) != 0 ? "rbc " : "") + ((mFlags & FLAG_INVALID_LUX) != 0 ? "invalid_lux " : "") + ((mFlags & FLAG_DOZE_SCALE) != 0 ? "doze_scale " : "") - + ((mFlags & FLAG_IDLE_CURVE) != 0 ? "idle_curve " : "") + ((mFlags & FLAG_LOW_POWER_MODE) != 0 ? "low_power_mode " : ""); } } diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java index bc443a8167ab..fc95d15ae6f4 100644 --- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java +++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java @@ -40,7 +40,8 @@ public final class BrightnessReason { public static final int REASON_SCREEN_OFF_BRIGHTNESS_SENSOR = 9; public static final int REASON_FOLLOWER = 10; public static final int REASON_OFFLOAD = 11; - public static final int REASON_MAX = REASON_OFFLOAD; + public static final int REASON_DOZE_INITIAL = 12; + public static final int REASON_MAX = REASON_DOZE_INITIAL; public static final int MODIFIER_DIMMED = 0x1; public static final int MODIFIER_LOW_POWER = 0x2; @@ -207,6 +208,8 @@ public final class BrightnessReason { return "follower"; case REASON_OFFLOAD: return "offload"; + case REASON_DOZE_INITIAL: + return "doze_initial"; default: return Integer.toString(reason); } diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java index 3e6e09da9753..8eaecef6e562 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java @@ -267,6 +267,23 @@ public class AutomaticBrightnessStrategy { } /** + * Get the automatic screen brightness based on the last observed lux reading. Used e.g. when + * entering doze - we disable the light sensor, invalidate the lux, but we still need to set + * the initial brightness in doze mode. + * @param brightnessEvent Event object to populate with details about why the specific + * brightness was chosen. + */ + public float getAutomaticScreenBrightnessBasedOnLastObservedLux( + BrightnessEvent brightnessEvent) { + float brightness = (mAutomaticBrightnessController != null) + ? mAutomaticBrightnessController + .getAutomaticScreenBrightnessBasedOnLastObservedLux(brightnessEvent) + : PowerManager.BRIGHTNESS_INVALID_FLOAT; + adjustAutomaticBrightnessStateIfValid(brightness); + return brightness; + } + + /** * Gets the auto-brightness adjustment flag change reason */ public int getAutoBrightnessAdjustmentReasonsFlags() { diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 796d8d73fc51..582058d21256 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -729,7 +729,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Map of uid -> UidStateCallbackInfo objects holding the data received from * {@link IUidObserver#onUidStateChanged(int, int, long, int)} callbacks. In order to avoid * creating a new object for every callback received, we hold onto the object created for each - * uid and reuse it. + * uid and reuse it until the uid stays alive. * * Note that the lock used for accessing this object should not be used for anything else and we * should not be acquiring new locks or doing any heavy work while this lock is held since this @@ -802,6 +802,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { Clock.systemUTC()); } + @VisibleForTesting + UidState getUidStateForTest(int uid) { + synchronized (mUidRulesFirstLock) { + return mUidState.get(uid); + } + } + static class Dependencies { final Context mContext; final NetworkStatsManager mNetworkStatsManager; @@ -1257,6 +1264,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override public void onUidGone(int uid, boolean disabled) { + synchronized (mUidStateCallbackInfos) { + mUidStateCallbackInfos.remove(uid); + } + // TODO: b/327058756 - Remove any pending UID_MSG_STATE_CHANGED on the handler. mUidEventHandler.obtainMessage(UID_MSG_GONE, uid, 0).sendToTarget(); } }; @@ -5918,7 +5929,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { void handleUidGone(int uid) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidGone"); try { - boolean updated; + final boolean updated; synchronized (mUidRulesFirstLock) { updated = removeUidStateUL(uid); } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index eed0eecb0a22..6857869e3776 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -1285,8 +1285,8 @@ public class ZenModeHelper { */ private static void updateZenDeviceEffects(ZenRule zenRule, @Nullable ZenDeviceEffects newEffects, boolean isFromApp, boolean updateBitmask) { + // Same as with ZenPolicy, supplying null effects means keeping the previous ones. if (newEffects == null) { - zenRule.zenDeviceEffects = null; return; } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index a1dac0456ff9..8cc242c10c16 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -701,7 +701,7 @@ final class InstallPackageHelper { pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId); pkgSetting.setFirstInstallTime(System.currentTimeMillis(), userId); // Clear any existing archive state. - mPm.mInstallerService.mPackageArchiver.clearArchiveState(packageName, userId); + mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgSetting, userId); mPm.mSettings.writePackageRestrictionsLPr(userId); mPm.mSettings.writeKernelMappingLPr(pkgSetting); installed = true; @@ -829,7 +829,8 @@ final class InstallPackageHelper { if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token); - if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED && doRestore) { + final boolean succeeded = request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED; + if (succeeded && doRestore) { // Pass responsibility to the Backup Manager. It will perform a // restore if appropriate, then pass responsibility back to the // Package Manager to run the post-install observer callbacks @@ -843,10 +844,27 @@ final class InstallPackageHelper { // need to be snapshotted or restored for the package. // // TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL. - if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) { + if (succeeded && !doRestore && update) { doRestore = performRollbackManagerRestore(userId, token, request); } + if (succeeded && !request.hasPostInstallRunnable()) { + boolean hasNeverBeenRestored = + packageSetting != null && packageSetting.isPendingRestore(); + request.setPostInstallRunnable(() -> { + // Permissions should be restored on each user that has the app installed for the + // first time, unless it's an unarchive install for an archived app, in which case + // the permissions should be restored on each user that has the app updated. + int[] userIdsToRestorePermissions = hasNeverBeenRestored + ? request.getUpdateBroadcastUserIds() + : request.getFirstTimeBroadcastUserIds(); + for (int restorePermissionUserId : userIdsToRestorePermissions) { + mPm.restorePermissionsAndUpdateRolesForNewUserInstall(request.getName(), + restorePermissionUserId); + } + }); + } + if (doRestore) { if (packageSetting != null) { synchronized (mPm.mLock) { @@ -2327,7 +2345,7 @@ final class InstallPackageHelper { installerPackageName); } // Clear any existing archive state. - mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgName, userId); + mPm.mInstallerService.mPackageArchiver.clearArchiveState(ps, userId); } else if (allUsers != null) { // The caller explicitly specified INSTALL_ALL_USERS flag. // Thus, updating the settings to install the app for all users. @@ -2351,7 +2369,7 @@ final class InstallPackageHelper { installerPackageName); } // Clear any existing archive state. - mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgName, + mPm.mInstallerService.mPackageArchiver.clearArchiveState(ps, currentUserId); } else { ps.setInstalled(false, currentUserId); @@ -2851,7 +2869,6 @@ final class InstallPackageHelper { mPm.notifyInstantAppPackageInstalled(request.getPkg().getPackageName(), request.getNewUsers()); - request.populateBroadcastUsers(); final int[] firstUserIds = request.getFirstTimeBroadcastUserIds(); if (request.getPkg().getStaticSharedLibraryName() == null) { @@ -2863,12 +2880,6 @@ final class InstallPackageHelper { mPm.mRequiredInstallerPackage, /* packageSender= */ mPm, launchedForRestore, killApp, update, archived); - // Work that needs to happen on first install within each user - for (int userId : firstUserIds) { - mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName, - userId); - } - if (request.isAllNewUsers() && !update) { mPm.notifyPackageAdded(packageName, request.getAppId()); } else { diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 4fb0c220708f..43075a232a23 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -692,6 +692,14 @@ final class InstallRequest { } } + public void setPostInstallRunnable(Runnable runnable) { + mPostInstallRunnable = runnable; + } + + public boolean hasPostInstallRunnable() { + return mPostInstallRunnable != null; + } + public void runPostInstallRunnable() { if (mPostInstallRunnable != null) { mPostInstallRunnable.run(); @@ -753,6 +761,7 @@ final class InstallRequest { public void setNewUsers(int[] newUsers) { mNewUsers = newUsers; + populateBroadcastUsers(); } public void setOriginPackage(String originPackage) { @@ -829,10 +838,11 @@ final class InstallRequest { } /** - * Determine the set of users who are adding this package for the first time vs. those who are - * seeing an update. + * Determine the set of users who are adding this package for the first time (aka "new" users) + * vs. those who are seeing an update (aka "update" users). The lists can be calculated as soon + * as the "new" users are set. */ - public void populateBroadcastUsers() { + private void populateBroadcastUsers() { assertScanResultExists(); mFirstTimeBroadcastUserIds = EMPTY_INT_ARRAY; mFirstTimeBroadcastInstantUserIds = EMPTY_INT_ARRAY; diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index d8d8dd2e57a9..3f9e989a5bba 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -2209,8 +2209,10 @@ public class LauncherAppsService extends SystemService { for (UserHandle user : users) { mPackageManagerInternal.forEachInstalledPackage(pkg -> { final String packageName = pkg.getPackageName(); - if (mPackageManagerInternal.getIncrementalStatesInfo(packageName, - Process.myUid(), user.getIdentifier()).isLoading()) { + final IncrementalStatesInfo info = + mPackageManagerInternal.getIncrementalStatesInfo(packageName, + Process.myUid(), user.getIdentifier()); + if (info != null && info.isLoading()) { mPackageManagerInternal.registerInstalledLoadingProgressCallback( packageName, new PackageLoadingProgressCallback(packageName, user), user.getIdentifier()); diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index cdd52a47e433..2b20bfd7a7c2 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -356,19 +356,34 @@ public class PackageArchiver { } void clearArchiveState(String packageName, int userId) { + final PackageSetting ps; synchronized (mPm.mLock) { - PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); - if (ps != null) { - ps.setArchiveState(/* archiveState= */ null, userId); + ps = mPm.mSettings.getPackageLPr(packageName); + } + clearArchiveState(ps, userId); + } + + void clearArchiveState(PackageSetting ps, int userId) { + synchronized (mPm.mLock) { + if (ps == null || ps.getUserStateOrDefault(userId).getArchiveState() == null) { + // No archive states to clear + return; } + if (DEBUG) { + Slog.e(TAG, "Clearing archive states for " + ps.getPackageName()); + } + ps.setArchiveState(/* archiveState= */ null, userId); } - File iconsDir = getIconsDir(packageName, userId); + File iconsDir = getIconsDir(ps.getPackageName(), userId); if (!iconsDir.exists()) { + if (DEBUG) { + Slog.e(TAG, "Icons are already deleted at " + iconsDir.getAbsolutePath()); + } return; } // TODO(b/319238030) Move this into installd. if (!FileUtils.deleteContentsAndDir(iconsDir)) { - Slog.e(TAG, "Failed to clean up archive files for " + packageName); + Slog.e(TAG, "Failed to clean up archive files for " + ps.getPackageName()); } else { if (DEBUG) { Slog.e(TAG, "Deleted icons at " + iconsDir.getAbsolutePath()); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index dadafd7f9438..3c256b1caa7d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -721,7 +721,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy; - @GuardedBy("mAvailableFeatures") private final ArrayMap<String, FeatureInfo> mAvailableFeatures; @Watched @@ -2983,13 +2982,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService public boolean hasSystemFeature(String name, int version) { // allow instant applications - synchronized (mAvailableFeatures) { - final FeatureInfo feat = mAvailableFeatures.get(name); - if (feat == null) { - return false; - } else { - return feat.version >= version; - } + final FeatureInfo feat = mAvailableFeatures.get(name); + if (feat == null) { + return false; + } else { + return feat.version >= version; } } @@ -5335,10 +5332,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() { // allow instant applications ArrayList<FeatureInfo> res; - synchronized (mAvailableFeatures) { - res = new ArrayList<>(mAvailableFeatures.size() + 1); - res.addAll(mAvailableFeatures.values()); - } + res = new ArrayList<>(mAvailableFeatures.size() + 1); + res.addAll(mAvailableFeatures.values()); final FeatureInfo fi = new FeatureInfo(); fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version", FeatureInfo.GL_ES_VERSION_UNDEFINED); @@ -6542,9 +6537,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mOverlayConfigSignaturePackage, mRecentsPackage); final ArrayMap<String, FeatureInfo> availableFeatures; - synchronized (mAvailableFeatures) { - availableFeatures = new ArrayMap<>(mAvailableFeatures); - } + availableFeatures = new ArrayMap<>(mAvailableFeatures); final ArraySet<String> protectedBroadcasts; synchronized (mProtectedBroadcasts) { protectedBroadcasts = new ArraySet<>(mProtectedBroadcasts); diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index 1c70af0a56ea..b18503d7d5cb 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -596,7 +596,7 @@ public class PackageInfoUtils { ai.requiredDisplayCategory = a.getRequiredDisplayCategory(); ai.requireContentUriPermissionFromCaller = a.getRequireContentUriPermissionFromCaller(); ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts()); - assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, userId); + assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, state, userId); return ai; } @@ -659,7 +659,7 @@ public class PackageInfoUtils { // Backwards compatibility, coerce to null if empty si.metaData = metaData.isEmpty() ? null : metaData; } - assignFieldsComponentInfoParsedMainComponent(si, s, pkgSetting, userId); + assignFieldsComponentInfoParsedMainComponent(si, s, pkgSetting, state, userId); return si; } @@ -710,7 +710,7 @@ public class PackageInfoUtils { pi.metaData = metaData.isEmpty() ? null : metaData; } pi.applicationInfo = applicationInfo; - assignFieldsComponentInfoParsedMainComponent(pi, p, pkgSetting, userId); + assignFieldsComponentInfoParsedMainComponent(pi, p, pkgSetting, state, userId); return pi; } @@ -903,8 +903,13 @@ public class PackageInfoUtils { private static void assignFieldsComponentInfoParsedMainComponent( @NonNull ComponentInfo info, @NonNull ParsedMainComponent component, - @NonNull PackageStateInternal pkgSetting, @UserIdInt int userId) { + @NonNull PackageStateInternal pkgSetting, @NonNull PackageUserStateInternal state, + @UserIdInt int userId) { assignFieldsComponentInfoParsedMainComponent(info, component); + // overwrite the enabled state with the current user state + info.enabled = PackageUserStateUtils.isEnabled(state, info.applicationInfo.enabled, + info.enabled, info.name, /* flags */ 0); + Pair<CharSequence, Integer> labelAndIcon = ParsedComponentStateUtils.getNonLocalizedLabelAndIcon(component, pkgSetting, userId); diff --git a/services/core/java/com/android/server/power/stats/PowerStatsSpan.java b/services/core/java/com/android/server/power/stats/PowerStatsSpan.java index 3b260ca0c3b9..4df919dffbe5 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsSpan.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsSpan.java @@ -57,7 +57,7 @@ public class PowerStatsSpan { * {@link #isCompatibleXmlFormat} to return true for all legacy versions * that are compatible with the new one. */ - private static final int VERSION = 1; + private static final int VERSION = 2; private static final String XML_TAG_METADATA = "metadata"; private static final String XML_ATTR_ID = "id"; diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 44a0547a6828..d08e272d76dd 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -304,9 +304,9 @@ final class AccessibilityController { Surface forceShowMagnifierSurface(int displayId) { final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.mMagnifedViewport.mWindow.setAlpha(DisplayMagnifier.MagnifiedViewport + displayMagnifier.mMagnifiedViewport.mWindow.setAlpha(DisplayMagnifier.MagnifiedViewport .ViewportWindow.AnimationController.MAX_ALPHA); - return displayMagnifier.mMagnifedViewport.mWindow.mSurface; + return displayMagnifier.mMagnifiedViewport.mWindow.mSurface; } return null; } @@ -463,6 +463,10 @@ final class AccessibilityController { } void drawMagnifiedRegionBorderIfNeeded(int displayId) { + if (Flags.magnificationAlwaysDrawFullscreenBorder()) { + return; + } + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { mAccessibilityTracing.logTrace( TAG + ".drawMagnifiedRegionBorderIfNeeded", @@ -614,7 +618,7 @@ final class AccessibilityController { private final Context mDisplayContext; private final WindowManagerService mService; - private final MagnifiedViewport mMagnifedViewport; + private final MagnifiedViewport mMagnifiedViewport; private final Handler mHandler; private final DisplayContent mDisplayContent; private final Display mDisplay; @@ -649,7 +653,8 @@ final class AccessibilityController { mDisplayContent = displayContent; mDisplay = display; mHandler = new MyHandler(mService.mH.getLooper()); - mMagnifedViewport = new MagnifiedViewport(); + mMagnifiedViewport = Flags.magnificationAlwaysDrawFullscreenBorder() + ? null : new MagnifiedViewport(); mAccessibilityTracing = AccessibilityController.getAccessibilityControllerInternal(mService); mLongAnimationDuration = mDisplayContext.getResources().getInteger( @@ -692,7 +697,9 @@ final class AccessibilityController { mMagnificationSpec.clear(); } - mMagnifedViewport.setShowMagnifiedBorderIfNeeded(); + if (!Flags.magnificationAlwaysDrawFullscreenBorder()) { + mMagnifiedViewport.setShowMagnifiedBorderIfNeeded(); + } } void setFullscreenMagnificationActivated(boolean activated) { @@ -701,8 +708,10 @@ final class AccessibilityController { FLAGS_MAGNIFICATION_CALLBACK, "activated=" + activated); } mIsFullscreenMagnificationActivated = activated; - mMagnifedViewport.setMagnifiedRegionBorderShown(activated, true); - mMagnifedViewport.showMagnificationBoundsIfNeeded(); + if (!Flags.magnificationAlwaysDrawFullscreenBorder()) { + mMagnifiedViewport.setMagnifiedRegionBorderShown(activated, true); + mMagnifiedViewport.showMagnificationBoundsIfNeeded(); + } } boolean isFullscreenMagnificationActivated() { @@ -737,7 +746,9 @@ final class AccessibilityController { } recomputeBounds(); - mMagnifedViewport.onDisplaySizeChanged(); + if (!Flags.magnificationAlwaysDrawFullscreenBorder()) { + mMagnifiedViewport.onDisplaySizeChanged(); + } mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED); } @@ -901,7 +912,10 @@ final class AccessibilityController { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK); } - mMagnifedViewport.destroyWindow(); + + if (!Flags.magnificationAlwaysDrawFullscreenBorder()) { + mMagnifiedViewport.destroyWindow(); + } } void drawMagnifiedRegionBorderIfNeeded() { @@ -909,7 +923,10 @@ final class AccessibilityController { mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded", FLAGS_MAGNIFICATION_CALLBACK); } - mMagnifedViewport.drawWindowIfNeeded(); + + if (!Flags.magnificationAlwaysDrawFullscreenBorder()) { + mMagnifiedViewport.drawWindowIfNeeded(); + } } void recomputeBounds() { @@ -1006,14 +1023,16 @@ final class AccessibilityController { } visibleWindows.clear(); - mMagnifedViewport.intersectWithDrawBorderInset(screenWidth, screenHeight); - + if (!Flags.magnificationAlwaysDrawFullscreenBorder()) { + mMagnifiedViewport.intersectWithDrawBorderInset(screenWidth, screenHeight); + } final boolean magnifiedChanged = !mOldMagnificationRegion.equals(mMagnificationRegion); if (magnifiedChanged) { - mMagnifedViewport.updateBorderDrawingStatus(screenWidth, screenHeight); - + if (!Flags.magnificationAlwaysDrawFullscreenBorder()) { + mMagnifiedViewport.updateBorderDrawingStatus(screenWidth, screenHeight); + } mOldMagnificationRegion.set(mMagnificationRegion); final SomeArgs args = SomeArgs.obtain(); args.arg1 = Region.obtain(mMagnificationRegion); @@ -1070,7 +1089,9 @@ final class AccessibilityController { } void dump(PrintWriter pw, String prefix) { - mMagnifedViewport.dump(pw, prefix); + if (!Flags.magnificationAlwaysDrawFullscreenBorder()) { + mMagnifiedViewport.dump(pw, prefix); + } } private final class MagnifiedViewport { @@ -1079,7 +1100,7 @@ final class AccessibilityController { private final int mHalfBorderWidth; private final int mDrawBorderInset; - private final ViewportWindow mWindow; + @Nullable private final ViewportWindow mWindow; private boolean mFullRedrawNeeded; @@ -1138,9 +1159,9 @@ final class AccessibilityController { void onDisplaySizeChanged() { // If fullscreen magnification is activated, hide the border immediately so // the user does not see strange artifacts during display size changed caused by - // rotation or folding/unfolding the device. In the rotation case, the screenshot - // used for rotation already has the border. After the rotation is complete - // we will show the border. + // rotation or folding/unfolding the device. In the rotation case, the + // screenshot used for rotation already has the border. After the rotation is + // completed we will show the border. if (isFullscreenMagnificationActivated()) { setMagnifiedRegionBorderShown(false, false); final long delay = (long) (mLongAnimationDuration @@ -1173,6 +1194,8 @@ final class AccessibilityController { mWindow.dump(pw, prefix); } + // TODO(291891390): Remove this class when we clean up the flag + // magnificationAlwaysDrawFullscreenBorder private final class ViewportWindow implements Runnable { private static final String SURFACE_TITLE = "Magnification Overlay"; @@ -1467,6 +1490,9 @@ final class AccessibilityController { public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1; public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3; public static final int MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED = 4; + + // TODO(291891390): Remove this field when we clean up the flag + // magnificationAlwaysDrawFullscreenBorder public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5; public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6; @@ -1495,7 +1521,9 @@ final class AccessibilityController { case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { synchronized (mService.mGlobalLock) { if (isFullscreenMagnificationActivated()) { - mMagnifedViewport.setMagnifiedRegionBorderShown(true, true); + if (!Flags.magnificationAlwaysDrawFullscreenBorder()) { + mMagnifiedViewport.setMagnifiedRegionBorderShown(true, true); + } mService.scheduleAnimationLocked(); } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d60fe4b7d7e1..5036fc646327 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -10059,7 +10059,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pendingResults, pendingNewIntents, configChangeFlags, new MergedConfiguration(getProcessGlobalConfiguration(), getMergedOverrideConfiguration()), - preserveWindow); + preserveWindow, getActivityWindowInfo()); final ActivityLifecycleItem lifecycleItem; if (andResume) { lifecycleItem = ResumeActivityItem.obtain(token, isTransitionForward(), diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 066d26222fbd..c137c54949e9 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1548,6 +1548,8 @@ class ActivityStarter { result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor, startFlags, options, inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid); + } catch (Exception ex) { + Slog.e(TAG, "Exception on startActivityInner", ex); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); startedActivityRootTask = handleStartResult(r, options, result, newTransition, diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index e0faddf171ca..aefa777a1db7 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -1893,7 +1893,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Check that we aren't reparenting to the same root task that the task is already in if (prevRootTask != null && prevRootTask.mTaskId == rootTaskId) { Slog.w(TAG, "Can not reparent to same root task, task=" + task - + " already in rootTaskId=" + rootTaskId); + + " already in rootTaskId=" + rootTaskId + " by " + Debug.getCallers(8)); return prevRootTask; } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 5cf9acdbc0d6..7f3df958664c 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -47,6 +47,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLO import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; @@ -1533,9 +1534,19 @@ public class DisplayPolicy { // Check the windows that overlap with system bars to determine system bars' appearance. if ((appWindow && attached == null && attrs.isFullscreen()) || attrs.type == TYPE_VOICE_INTERACTION) { - // Record the top-fullscreen-app-window which will be used to determine system UI + + // If this is the exiting starting window, don't let it control the system bars. + // The app window behind it should be the controlling window instead. Reason: when an + // activity starts another activity behind a starting window, the app window of the + // first activity will lose the window focus. And then mTopFullscreenOpaqueWindowState + // will control the system bars. The logic here is to let first app window keep + // controlling system bars until the second app window is ready. + final boolean exitingStartingWindow = + attrs.type == TYPE_APPLICATION_STARTING && win.mAnimatingExit; + + // Record the top-fullscreen-app-window which will be used to determine the system UI // controlling window. - if (mTopFullscreenOpaqueWindowState == null) { + if (mTopFullscreenOpaqueWindowState == null && !exitingStartingWindow) { mTopFullscreenOpaqueWindowState = win; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 2bee095e7f46..1353ff09b292 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3750,8 +3750,7 @@ class Task extends TaskFragment { // Boost the adjacent TaskFragment for dimmer if needed. final TaskFragment taskFragment = wc.asTaskFragment(); - if (taskFragment != null && taskFragment.isEmbedded() - && taskFragment.isVisibleRequested()) { + if (taskFragment != null && taskFragment.isEmbedded()) { final TaskFragment adjacentTf = taskFragment.getAdjacentTaskFragment(); if (adjacentTf != null && adjacentTf.shouldBoostDimmer()) { adjacentTf.assignLayer(t, layer++); diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java index 723c52f37574..ca72638fd0ff 100644 --- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java @@ -150,7 +150,8 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ @Override public void onFinalErrorReceived(ComponentName componentName, String errorType, String message) { - respondToClientWithErrorAndFinish(errorType, message); + Slog.d(TAG, "onFinalErrorReceived"); + respondToFinalReceiverWithFailureAndFinish(this.mFinalResponseReceiver, errorType, message); } @Override @@ -163,6 +164,13 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ message = "The UI was interrupted - please try again."; } mRequestSessionMetric.collectFrameworkException(exception); + respondToFinalReceiverWithFailureAndFinish(finalResponseReceiver, exception, message); + } + + private void respondToFinalReceiverWithFailureAndFinish( + ResultReceiver finalResponseReceiver, + String exception, String message + ) { if (finalResponseReceiver != null) { Bundle resultData = new Bundle(); resultData.putStringArray( @@ -170,16 +178,16 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ new String[] {exception, message}); finalResponseReceiver.send(Constants.FAILURE_CREDMAN_SELECTOR, resultData); } else { - respondToClientWithErrorAndFinish(exception, message); + Slog.w(TAG, "onUiCancellation called but finalResponseReceiver not found"); } + finishSession(/*propagateCancellation=*/false); } @Override public void onUiSelectorInvocationFailure() { String exception = GetCandidateCredentialsException.TYPE_NO_CREDENTIAL; mRequestSessionMetric.collectFrameworkException(exception); - respondToClientWithErrorAndFinish(exception, - "No credentials available."); + // TODO(): Propagate through final receiver } @Override diff --git a/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java b/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java index 21ac9e44de65..bc8c2b07f427 100644 --- a/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java +++ b/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java @@ -82,7 +82,7 @@ public class PendingIntentResultHandler { if (resultData == null) { return null; } - return resultData.getParcelableExtra( + return resultData.getSerializableExtra( CredentialProviderService.EXTRA_CREATE_CREDENTIAL_EXCEPTION, CreateCredentialException.class); } @@ -94,7 +94,7 @@ public class PendingIntentResultHandler { if (resultData == null) { return null; } - return resultData.getParcelableExtra( + return resultData.getSerializableExtra( CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, GetCredentialException.class); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index 418b78cd696b..9c9aeeaadb71 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -16,6 +16,7 @@ package com.android.server.display; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; @@ -1016,4 +1017,34 @@ public class AutomaticBrightnessControllerTest { listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500)); assertEquals(500, mController.getAmbientLux(), EPSILON); } + + @Test + public void testBrightnessBasedOnLastObservedLux() throws Exception { + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), + eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + // Set up system to return 0.3f as a brightness value + float lux = 100.0f; + // Brightness as float (from 0.0f to 1.0f) + float normalizedBrightness = 0.3f; + when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux); + when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux); + when(mBrightnessMappingStrategy.getBrightness(eq(lux), eq(null), anyInt())) + .thenReturn(normalizedBrightness); + when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT); + when(mBrightnessThrottler.isThrottled()).thenReturn(true); + + // Send a new sensor value, disable the sensor and verify + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux)); + mController.configure(AUTO_BRIGHTNESS_DISABLED, /* configuration= */ null, + /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ true); + assertEquals(normalizedBrightness, + mController.getAutomaticScreenBrightnessBasedOnLastObservedLux( + /* brightnessEvent= */ null), EPSILON); + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 807774f90655..acddc9d22953 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -1665,6 +1665,35 @@ public final class DisplayPowerControllerTest { /* luxTimestamps= */ any()); } + @Test + public void testInitialDozeBrightness() { + when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); + when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true); + mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false); + float brightness = 0.277f; + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.automaticBrightnessController + .getAutomaticScreenBrightnessBasedOnLastObservedLux(any(BrightnessEvent.class))) + .thenReturn(brightness); + when(mHolder.hbmController.getCurrentBrightnessMax()) + .thenReturn(PowerManager.BRIGHTNESS_MAX); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.animator).animateTo(eq(brightness), + /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(), + /* ignoreAnimationLimits= */ anyBoolean()); + } + /** * Creates a mock and registers it to {@link LocalServices}. */ diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java index c0c63c69add8..060f99b4317a 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java @@ -16,9 +16,12 @@ package com.android.server.display.brightness; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; + import static org.junit.Assert.assertEquals; import android.hardware.display.BrightnessInfo; +import android.view.Display; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -39,6 +42,7 @@ public final class BrightnessEventTest { mBrightnessEvent.setReason( getReason(BrightnessReason.REASON_DOZE, BrightnessReason.MODIFIER_LOW_POWER)); mBrightnessEvent.setPhysicalDisplayId("test"); + mBrightnessEvent.setDisplayState(Display.STATE_ON); mBrightnessEvent.setLux(100.0f); mBrightnessEvent.setPreThresholdLux(150.0f); mBrightnessEvent.setTime(System.currentTimeMillis()); @@ -55,6 +59,7 @@ public final class BrightnessEventTest { mBrightnessEvent.setAdjustmentFlags(0); mBrightnessEvent.setAutomaticBrightnessEnabled(true); mBrightnessEvent.setDisplayBrightnessStrategyName(DISPLAY_BRIGHTNESS_STRATEGY_NAME); + mBrightnessEvent.setAutoBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); } @Test @@ -69,20 +74,20 @@ public final class BrightnessEventTest { public void testToStringWorksAsExpected() { String actualString = mBrightnessEvent.toString(false); String expectedString = - "BrightnessEvent: disp=1, physDisp=test, brt=0.6, initBrt=25.0, rcmdBrt=0.6," - + " preBrt=NaN, lux=100.0, preLux=150.0, hbmMax=0.62, hbmMode=off, rbcStrength=-1," - + " thrmMax=0.65, powerFactor=0.2, wasShortTermModelActive=true, flags=," - + " reason=doze [ low_pwr ], autoBrightness=true, strategy=" - + DISPLAY_BRIGHTNESS_STRATEGY_NAME; + "BrightnessEvent: disp=1, physDisp=test, displayState=ON, brt=0.6, initBrt=25.0," + + " rcmdBrt=0.6, preBrt=NaN, lux=100.0, preLux=150.0, hbmMax=0.62, hbmMode=off," + + " rbcStrength=-1, thrmMax=0.65, powerFactor=0.2, wasShortTermModelActive=true," + + " flags=, reason=doze [ low_pwr ], autoBrightness=true, strategy=" + + DISPLAY_BRIGHTNESS_STRATEGY_NAME + ", autoBrightnessMode=idle"; assertEquals(expectedString, actualString); } @Test public void testFlagsToString() { mBrightnessEvent.reset(); - mBrightnessEvent.setFlags(mBrightnessEvent.getFlags() | BrightnessEvent.FLAG_IDLE_CURVE); + mBrightnessEvent.setFlags(mBrightnessEvent.getFlags() | BrightnessEvent.FLAG_RBC); String actualString = mBrightnessEvent.flagsToString(); - String expectedString = "idle_curve "; + String expectedString = "rbc "; assertEquals(expectedString, actualString); } @@ -90,10 +95,10 @@ public final class BrightnessEventTest { public void testFlagsToString_multipleFlags() { mBrightnessEvent.reset(); mBrightnessEvent.setFlags(mBrightnessEvent.getFlags() - | BrightnessEvent.FLAG_IDLE_CURVE + | BrightnessEvent.FLAG_RBC | BrightnessEvent.FLAG_LOW_POWER_MODE); String actualString = mBrightnessEvent.flagsToString(); - String expectedString = "idle_curve low_power_mode "; + String expectedString = "rbc low_power_mode "; assertEquals(expectedString, actualString); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index 5408e1143330..886780655de2 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -359,11 +359,17 @@ public class AutomaticBrightnessStrategyTest { AutomaticBrightnessController.class); when(automaticBrightnessController.getAutomaticScreenBrightness(any(BrightnessEvent.class))) .thenReturn(automaticScreenBrightness); + when(automaticBrightnessController.getAutomaticScreenBrightnessBasedOnLastObservedLux( + any(BrightnessEvent.class))) + .thenReturn(automaticScreenBrightness); mAutomaticBrightnessStrategy.setAutomaticBrightnessController( automaticBrightnessController); assertEquals(automaticScreenBrightness, mAutomaticBrightnessStrategy.getAutomaticScreenBrightness( new BrightnessEvent(DISPLAY_ID)), 0.0f); + assertEquals(automaticScreenBrightness, + mAutomaticBrightnessStrategy.getAutomaticScreenBrightnessBasedOnLastObservedLux( + new BrightnessEvent(DISPLAY_ID)), 0.0f); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java index 009bfb7efca6..87fe6cf8f283 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java @@ -813,6 +813,18 @@ public class MagnificationConnectionManagerTest { anyBoolean()); } + @Test + public void onFullscreenMagnificationActivationChanged_hasConnection_notifyActivatedState() + throws RemoteException { + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + + mMagnificationConnectionManager + .onFullscreenMagnificationActivationChanged(TEST_DISPLAY, /* activated= */ true); + + verify(mMockConnection.getConnection()) + .onFullscreenMagnificationActivationChanged(eq(TEST_DISPLAY), eq(true)); + } + private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) { final int len = pointersLocation.length; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java index 07f3036410a0..2120b2e8e1f3 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java @@ -131,6 +131,14 @@ public class MagnificationConnectionWrapperTest { } @Test + public void onFullscreenMagnificationActivationChanged() throws RemoteException { + mConnectionWrapper + .onFullscreenMagnificationActivationChanged(TEST_DISPLAY, /* activated= */ true); + verify(mConnection) + .onFullscreenMagnificationActivationChanged(eq(TEST_DISPLAY), eq(true)); + } + + @Test public void setMirrorWindowCallback() throws RemoteException { mConnectionWrapper.setConnectionCallback(mCallback); verify(mConnection).setConnectionCallback(mCallback); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index a0c4b5e26c3f..1a51c45e2ef6 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -53,6 +53,10 @@ import android.hardware.display.DisplayManagerInternal; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.testing.DexmakerShareClassLoaderRule; @@ -73,6 +77,7 @@ import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.wm.WindowManagerInternal; +import com.android.window.flags.Flags; import org.junit.After; import org.junit.Before; @@ -91,6 +96,9 @@ import org.mockito.stubbing.Answer; @RunWith(AndroidJUnit4.class) public class MagnificationControllerTest { + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; private static final int TEST_SERVICE_ID = 1; private static final Region INITIAL_SCREEN_MAGNIFICATION_REGION = @@ -1263,6 +1271,27 @@ public class MagnificationControllerTest { verify(mService).changeMagnificationMode(TEST_DISPLAY, MODE_WINDOW); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_MAGNIFICATION_ALWAYS_DRAW_FULLSCREEN_BORDER) + public void onFullscreenMagnificationActivationState_systemUiBorderFlagOn_notifyConnection() { + mMagnificationController.onFullScreenMagnificationActivationState( + TEST_DISPLAY, /* activated= */ true); + + verify(mMagnificationConnectionManager) + .onFullscreenMagnificationActivationChanged(TEST_DISPLAY, /* activated= */ true); + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_MAGNIFICATION_ALWAYS_DRAW_FULLSCREEN_BORDER) + public void + onFullscreenMagnificationActivationState_systemUiBorderFlagOff_neverNotifyConnection() { + mMagnificationController.onFullScreenMagnificationActivationState( + TEST_DISPLAY, /* activated= */ true); + + verify(mMagnificationConnectionManager, never()) + .onFullscreenMagnificationActivationChanged(TEST_DISPLAY, /* activated= */ true); + } + private void setMagnificationEnabled(int mode) throws RemoteException { setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y); } diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index a529382bfb26..b1e62f9c9a82 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -149,6 +149,7 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; import android.net.NetworkStateSnapshot; import android.net.NetworkTemplate; import android.net.TelephonyNetworkSpecifier; @@ -1620,6 +1621,12 @@ public class NetworkPolicyManagerServiceTest { verify(mActivityManagerInternal).notifyNetworkPolicyRulesUpdated(UID_A, procStateSeq); } + private void callAndWaitOnUidGone(int uid) throws Exception { + // The disabled argument is used only for ephemeral apps and does not matter here. + mUidObserver.onUidGone(uid, false /* disabled */); + waitForUidEventHandlerIdle(); + } + private void callAndWaitOnUidStateChanged(int uid, int procState, long procStateSeq) throws Exception { callAndWaitOnUidStateChanged(uid, procState, procStateSeq, @@ -2250,6 +2257,15 @@ public class NetworkPolicyManagerServiceTest { assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); } + private boolean isUidState(int uid, int procState, int procStateSeq, int capability) { + final NetworkPolicyManager.UidState uidState = mService.getUidStateForTest(uid); + if (uidState == null) { + return false; + } + return uidState.uid == uid && uidState.procStateSeq == procStateSeq + && uidState.procState == procState && uidState.capability == capability; + } + @Ignore("Temporarily disabled until the feature is enabled") @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) @@ -2371,6 +2387,31 @@ public class NetworkPolicyManagerServiceTest { } @Test + public void testUidStateChangeAfterUidGone() throws Exception { + final int testProcStateSeq = 51; + final int testProcState = PROCESS_STATE_IMPORTANT_FOREGROUND; + + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // First callback for uid. + callOnUidStatechanged(UID_B, testProcState, testProcStateSeq, PROCESS_CAPABILITY_NONE); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + assertTrue(isUidState(UID_B, testProcState, testProcStateSeq, PROCESS_CAPABILITY_NONE)); + + callAndWaitOnUidGone(UID_B); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // Even though the procState is the same, the uid had exited - so this should be + // processed as a fresh callback. + callOnUidStatechanged(UID_B, testProcState, testProcStateSeq + 1, + PROCESS_CAPABILITY_NONE); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + assertTrue(isUidState(UID_B, testProcState, testProcStateSeq + 1, PROCESS_CAPABILITY_NONE)); + } + + @Test public void testLowPowerStandbyAllowlist() throws Exception { // Chain background is also enabled but these procstates are important enough to be exempt. callAndWaitOnUidStateChanged(UID_A, PROCESS_STATE_TOP, 0); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 8c50ef406ec6..8c2fd1013056 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -4420,13 +4420,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { @EnableFlags(Flags.FLAG_MODES_API) public void updateAutomaticZenRule_nullDeviceEffectsUpdate() { // Adds a starting rule with empty zen policies and device effects + ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) - .setDeviceEffects(new ZenDeviceEffects.Builder().build()) + .setDeviceEffects(zde) .build(); // Adds the rule using the app, to avoid having any user modified bits set. String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID); - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId); AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase) // Sets Device Effects to null @@ -4437,10 +4437,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { // user modified, it can be updated. mZenModeHelper.updateAutomaticZenRule(ruleId, azr, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID); - rule = mZenModeHelper.getAutomaticZenRule(ruleId); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId); - // When AZR's ZenDeviceEffects is null, the updated rule's device effects will be null. - assertThat(rule.getDeviceEffects()).isNull(); + // When AZR's ZenDeviceEffects is null, the updated rule's device effects are kept. + assertThat(rule.getDeviceEffects()).isEqualTo(zde); } @Test diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java index 417fbd06be66..0d5bf95d959d 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java @@ -67,6 +67,7 @@ public class VibratorControlServiceTest { @Mock private PackageManagerInternal mPackageManagerInternalMock; + private TestLooper mTestLooper; private FakeVibratorController mFakeVibratorController; private VibratorControlService mVibratorControlService; private VibrationSettings mVibrationSettings; @@ -74,6 +75,7 @@ public class VibratorControlServiceTest { @Before public void setUp() throws Exception { + mTestLooper = new TestLooper(); when(mPackageManagerInternalMock.getSystemUiServiceComponent()) .thenReturn(new ComponentName("", "")); LocalServices.removeServiceForTest(PackageManagerInternal.class); @@ -83,7 +85,7 @@ public class VibratorControlServiceTest { mVibrationSettings = new VibrationSettings( ApplicationProvider.getApplicationContext(), new Handler(testLooper.getLooper())); - mFakeVibratorController = new FakeVibratorController(); + mFakeVibratorController = new FakeVibratorController(mTestLooper.getLooper()); mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mMockVibrationScaler, mVibrationSettings, mLock); } @@ -108,13 +110,13 @@ public class VibratorControlServiceTest { @Test public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest() throws RemoteException { - FakeVibratorController fakeController1 = new FakeVibratorController(); - FakeVibratorController fakeController2 = new FakeVibratorController(); - mVibratorControlService.registerVibratorController(fakeController1); - mVibratorControlService.unregisterVibratorController(fakeController2); + FakeVibratorController controller1 = new FakeVibratorController(mTestLooper.getLooper()); + FakeVibratorController controller2 = new FakeVibratorController(mTestLooper.getLooper()); + mVibratorControlService.registerVibratorController(controller1); + mVibratorControlService.unregisterVibratorController(controller2); verifyZeroInteractions(mMockVibrationScaler); - assertThat(fakeController1.isLinkedToDeath).isTrue(); + assertThat(controller1.isLinkedToDeath).isTrue(); } @Test diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java index 79abe21a301d..db823d649cb6 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java @@ -18,23 +18,26 @@ package com.android.server.vibrator; import static com.google.common.truth.Truth.assertThat; -import android.os.RemoteException; +import android.os.test.TestLooper; import org.junit.Before; import org.junit.Test; public class VibratorControllerHolderTest { - private final FakeVibratorController mFakeVibratorController = new FakeVibratorController(); + private TestLooper mTestLooper; + private FakeVibratorController mFakeVibratorController; private VibratorControllerHolder mVibratorControllerHolder; @Before public void setUp() throws Exception { + mTestLooper = new TestLooper(); + mFakeVibratorController = new FakeVibratorController(mTestLooper.getLooper()); mVibratorControllerHolder = new VibratorControllerHolder(); } @Test - public void testSetVibratorController_linksVibratorControllerToDeath() throws RemoteException { + public void testSetVibratorController_linksVibratorControllerToDeath() { mVibratorControllerHolder.setVibratorController(mFakeVibratorController); assertThat(mVibratorControllerHolder.getVibratorController()) .isEqualTo(mFakeVibratorController); @@ -42,8 +45,7 @@ public class VibratorControllerHolderTest { } @Test - public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath() - throws RemoteException { + public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath() { mVibratorControllerHolder.setVibratorController(mFakeVibratorController); mVibratorControllerHolder.setVibratorController(null); assertThat(mFakeVibratorController.isLinkedToDeath).isFalse(); @@ -51,8 +53,7 @@ public class VibratorControllerHolderTest { } @Test - public void testBinderDied_withValidController_unlinksVibratorControllerToDeath() - throws RemoteException { + public void testBinderDied_withValidController_unlinksVibratorControllerToDeath() { mVibratorControllerHolder.setVibratorController(mFakeVibratorController); mVibratorControllerHolder.binderDied(mFakeVibratorController); assertThat(mFakeVibratorController.isLinkedToDeath).isFalse(); @@ -60,10 +61,10 @@ public class VibratorControllerHolderTest { } @Test - public void testBinderDied_withInvalidController_ignoresRequest() - throws RemoteException { + public void testBinderDied_withInvalidController_ignoresRequest() { mVibratorControllerHolder.setVibratorController(mFakeVibratorController); - FakeVibratorController imposterVibratorController = new FakeVibratorController(); + FakeVibratorController imposterVibratorController = + new FakeVibratorController(mTestLooper.getLooper()); mVibratorControllerHolder.binderDied(imposterVibratorController); assertThat(mFakeVibratorController.isLinkedToDeath).isTrue(); assertThat(mVibratorControllerHolder.getVibratorController()) diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index ed89ccf07453..d2ad61f2ba9f 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -97,7 +97,6 @@ import android.view.InputDevice; import android.view.flags.Flags; import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; import com.android.internal.app.IBatteryStats; import com.android.internal.util.FrameworkStatsLog; @@ -117,6 +116,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -128,9 +128,7 @@ import java.util.function.Predicate; public class VibratorManagerServiceTest { private static final int TEST_TIMEOUT_MILLIS = 1_000; - // Time to allow for a cancellation to complete (notably including system ramp down), but not so - // long that tests easily get really slow or flaky. If a vibration is close to this, it should - // be cancelled in the body of the individual test. + // Time to allow for a cancellation to complete and the vibrators to become idle. private static final int CLEANUP_TIMEOUT_MILLIS = 100; private static final int UID = Process.ROOT_UID; private static final int VIRTUAL_DEVICE_ID = 1; @@ -186,8 +184,8 @@ public class VibratorManagerServiceTest { private AudioManager mAudioManagerMock; private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>(); - - private SparseArray<VibrationEffect> mHapticFeedbackVibrationMap = new SparseArray<>(); + private final SparseArray<VibrationEffect> mHapticFeedbackVibrationMap = new SparseArray<>(); + private final List<HalVibration> mPendingVibrations = new ArrayList<>(); private VibratorManagerService mService; private Context mContextSpy; @@ -207,7 +205,7 @@ public class VibratorManagerServiceTest { mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mVibrationConfig = new VibrationConfig(mContextSpy.getResources()); - mFakeVibratorController = new FakeVibratorController(); + mFakeVibratorController = new FakeVibratorController(mTestLooper.getLooper()); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); @@ -253,12 +251,15 @@ public class VibratorManagerServiceTest { @After public void tearDown() throws Exception { if (mService != null) { - // Wait until all vibrators have stopped vibrating, with a bit of flexibility for tests - // that just do a click or have cancelled at the end (waiting for ramp-down). - // - // Note: if a test is flaky here, check whether a VibrationEffect duration is close to - // CLEANUP_TIMEOUT_MILLIS - in which case it's probably best to just cancel that effect - // explicitly at the end of the test case (rather than letting it run and race flakily). + if (!mPendingVibrations.stream().allMatch(HalVibration::hasEnded)) { + // Cancel any pending vibration from tests. + cancelVibrate(mService); + for (HalVibration vibration : mPendingVibrations) { + vibration.waitForEnd(); + } + } + // Wait until all vibrators have stopped vibrating, waiting for ramp-down. + // Note: if a test is flaky here something is wrong with the vibration finalization. assertTrue(waitUntil(s -> { for (int vibratorId : mService.getVibratorIds()) { if (s.isVibrating(vibratorId)) { @@ -266,7 +267,7 @@ public class VibratorManagerServiceTest { } } return true; - }, mService, CLEANUP_TIMEOUT_MILLIS)); + }, mService, mVibrationConfig.getRampDownDurationMs() + CLEANUP_TIMEOUT_MILLIS)); } LocalServices.removeServiceForTest(PackageManagerInternal.class); @@ -323,21 +324,26 @@ public class VibratorManagerServiceTest { (VibratorManagerService.ExternalVibratorService) service; } else if (service instanceof VibratorControlService) { mVibratorControlService = (VibratorControlService) service; + mFakeVibratorController.setVibratorControlService( + mVibratorControlService); } } + @Override HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider( Resources resources, VibratorInfo vibratorInfo) { return new HapticFeedbackVibrationProvider( resources, vibratorInfo, mHapticFeedbackVibrationMap); } + @Override VibratorControllerHolder createVibratorControllerHolder() { VibratorControllerHolder holder = new VibratorControllerHolder(); holder.setVibratorController(mFakeVibratorController); return holder; } + @Override boolean isServiceDeclared(String name) { return true; } @@ -486,8 +492,9 @@ public class VibratorManagerServiceTest { InOrder inOrderVerifier = inOrder(listenerMock); // First notification done when listener is registered. inOrderVerifier.verify(listenerMock).onVibrating(eq(false)); + // Vibrator on notification done before vibration ended, no need to wait. inOrderVerifier.verify(listenerMock).onVibrating(eq(true)); - // The last notification is after the vibration has completed. + // Vibrator off notification done after vibration completed, wait for notification. inOrderVerifier.verify(listenerMock, timeout(TEST_TIMEOUT_MILLIS)).onVibrating(eq(false)); inOrderVerifier.verifyNoMoreInteractions(); @@ -517,6 +524,7 @@ public class VibratorManagerServiceTest { InOrder inOrderVerifier = inOrder(listenerMock); // First notification done when listener is registered. inOrderVerifier.verify(listenerMock).onVibrating(eq(false)); + // Vibrator on notification done before vibration ended, no need to wait. inOrderVerifier.verify(listenerMock).onVibrating(eq(true)); inOrderVerifier.verify(listenerMock, atLeastOnce()).asBinder(); // unregister inOrderVerifier.verifyNoMoreInteractions(); @@ -541,7 +549,6 @@ public class VibratorManagerServiceTest { verify(listeners[0]).onVibrating(eq(true)); verify(listeners[1]).onVibrating(eq(true)); verify(listeners[2], never()).onVibrating(eq(true)); - cancelVibrate(service); // Clean up long-ish effect. } @Test @@ -889,21 +896,24 @@ public class VibratorManagerServiceTest { eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString()); } - @FlakyTest @Test public void vibrate_withOngoingRepeatingVibration_ignoresEffect() throws Exception { mockVibrators(1); - mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorManagerService service = createSystemReadyService(); VibrationEffect repeatingEffect = VibrationEffect.createWaveform( - new long[]{10_000, 10_000}, new int[]{128, 255}, 1); - vibrate(service, repeatingEffect, new VibrationAttributes.Builder().setUsage( - VibrationAttributes.USAGE_UNKNOWN).build()); + new long[]{10, 10_000}, new int[]{128, 255}, 1); + vibrate(service, repeatingEffect, + new VibrationAttributes.Builder() + .setUsage(VibrationAttributes.USAGE_UNKNOWN) + .build()); - // VibrationThread will start this vibration async, wait until it has started. - assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), - service, TEST_TIMEOUT_MILLIS)); + // VibrationThread will start this vibration async. + // Wait until second step started to ensure the noteVibratorOn was triggered. + assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service, + TEST_TIMEOUT_MILLIS)); vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), HAPTIC_FEEDBACK_ATTRS); @@ -914,9 +924,8 @@ public class VibratorManagerServiceTest { // The second vibration shouldn't have recorded that the vibrators were turned on. verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong()); // No segment played is the prebaked CLICK from the second vibration. - assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream() + assertFalse(fakeVibrator.getAllEffectSegments().stream() .anyMatch(PrebakedSegment.class::isInstance)); - cancelVibrate(service); // Clean up repeating effect. } @Test @@ -930,11 +939,13 @@ public class VibratorManagerServiceTest { VibratorManagerService service = createSystemReadyService(); VibrationEffect repeatingEffect = VibrationEffect.createWaveform( - new long[]{10, 10_000}, new int[]{255, 0}, 1); + new long[]{10, 10_000}, new int[]{128, 255}, 1); vibrate(service, repeatingEffect, ALARM_ATTRS); - // VibrationThread will start this vibration async, wait until the off waveform step. - assertTrue(waitUntil(s -> fakeVibrator.getOffCount() > 0, service, TEST_TIMEOUT_MILLIS)); + // VibrationThread will start this vibration async. + // Wait until second step started to ensure the noteVibratorOn was triggered. + assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service, + TEST_TIMEOUT_MILLIS)); // Cancel vibration right before requesting a new one. // This should trigger slow IVibrator.off before setting the vibration status to cancelled. @@ -942,6 +953,8 @@ public class VibratorManagerServiceTest { vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS); + // The second vibration should have recorded that the vibrators were turned on. + verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong()); // Check that second vibration was played. assertTrue(fakeVibrator.getAllEffectSegments().stream() .anyMatch(PrebakedSegment.class::isInstance)); @@ -951,45 +964,47 @@ public class VibratorManagerServiceTest { public void vibrate_withNewSameImportanceVibrationAndBothRepeating_cancelsOngoingEffect() throws Exception { mockVibrators(1); - mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorManagerService service = createSystemReadyService(); VibrationEffect repeatingEffect = VibrationEffect.createWaveform( - new long[]{10_000, 10_000}, new int[]{128, 255}, 1); + new long[]{10, 10_000}, new int[]{128, 255}, 1); vibrate(service, repeatingEffect, ALARM_ATTRS); - // VibrationThread will start this vibration async, wait until it has started. - assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), - service, TEST_TIMEOUT_MILLIS)); + // VibrationThread will start this vibration async. + // Wait until second step started to ensure the noteVibratorOn was triggered. + assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service, + TEST_TIMEOUT_MILLIS)); VibrationEffect repeatingEffect2 = VibrationEffect.createWaveform( - new long[]{10_000, 10_000}, new int[]{128, 255}, 1); + new long[]{10, 10_000}, new int[]{255, 128}, 1); vibrate(service, repeatingEffect2, ALARM_ATTRS); - // VibrationThread will start this vibration async, so wait before checking it started. - assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() == 2, - service, TEST_TIMEOUT_MILLIS)); + // VibrationThread will start this vibration async. + // Wait until second step started to ensure the noteVibratorOn was triggered. + assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 4, service, + TEST_TIMEOUT_MILLIS)); // The second vibration should have recorded that the vibrators were turned on. verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong()); - - cancelVibrate(service); // Clean up repeating effect. } - @FlakyTest @Test public void vibrate_withNewSameImportanceVibrationButOngoingIsRepeating_ignoreNewVibration() throws Exception { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorManagerService service = createSystemReadyService(); VibrationEffect repeatingEffect = VibrationEffect.createWaveform( - new long[]{10, 10_000}, new int[]{255, 0}, 1); + new long[]{10, 10_000}, new int[]{128, 255}, 1); vibrate(service, repeatingEffect, ALARM_ATTRS); - // VibrationThread will start this vibration async, wait until it has started. - assertTrue(waitUntil(s -> !fakeVibrator.getAllEffectSegments().isEmpty(), service, + // VibrationThread will start this vibration async. + // Wait until second step started to ensure the noteVibratorOn was triggered. + assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service, TEST_TIMEOUT_MILLIS)); vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), @@ -998,53 +1013,55 @@ public class VibratorManagerServiceTest { // The second vibration shouldn't have recorded that the vibrators were turned on. verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong()); // The second vibration shouldn't have played any prebaked segment. - assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream() + assertFalse(fakeVibrator.getAllEffectSegments().stream() .anyMatch(PrebakedSegment.class::isInstance)); - cancelVibrate(service); // Clean up long effect. } @Test public void vibrate_withNewUnknownUsageVibrationAndRepeating_cancelsOngoingEffect() throws Exception { mockVibrators(1); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorManagerService service = createSystemReadyService(); VibrationEffect repeatingEffect = VibrationEffect.createWaveform( - new long[]{10_000, 10_000}, new int[]{128, 255}, 1); + new long[]{10, 10_000}, new int[]{128, 255}, 1); vibrate(service, repeatingEffect, ALARM_ATTRS); - // VibrationThread will start this vibration async, wait until it has started. - assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), - service, TEST_TIMEOUT_MILLIS)); + // VibrationThread will start this vibration async. + // Wait until second step started to ensure the noteVibratorOn was triggered. + assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service, + TEST_TIMEOUT_MILLIS)); VibrationEffect repeatingEffect2 = VibrationEffect.createWaveform( - new long[]{10_000, 10_000}, new int[]{128, 255}, 1); + new long[]{10, 10_000}, new int[]{255, 128}, 1); vibrate(service, repeatingEffect2, UNKNOWN_ATTRS); - // VibrationThread will start this vibration async, so wait before checking it started. - assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() == 2, - service, TEST_TIMEOUT_MILLIS)); + // VibrationThread will start this vibration async. + // Wait until second step started to ensure the noteVibratorOn was triggered. + assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 4, service, + TEST_TIMEOUT_MILLIS)); // The second vibration should have recorded that the vibrators were turned on. verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong()); - - cancelVibrate(service); // Clean up repeating effect. } - @FlakyTest @Test public void vibrate_withNewUnknownUsageVibrationAndNotRepeating_ignoreNewVibration() throws Exception { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorManagerService service = createSystemReadyService(); VibrationEffect alarmEffect = VibrationEffect.createWaveform( - new long[]{10, 10_000}, new int[]{255, 0}, -1); + new long[]{10, 10_000}, new int[]{128, 255}, -1); vibrate(service, alarmEffect, ALARM_ATTRS); - // VibrationThread will start this vibration async, wait until it has started. - assertTrue(waitUntil(s -> !fakeVibrator.getAllEffectSegments().isEmpty(), service, + // VibrationThread will start this vibration async. + // Wait until second step started to ensure the noteVibratorOn was triggered. + assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service, TEST_TIMEOUT_MILLIS)); vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), @@ -1053,23 +1070,24 @@ public class VibratorManagerServiceTest { // The second vibration shouldn't have recorded that the vibrators were turned on. verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong()); // The second vibration shouldn't have played any prebaked segment. - assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream() + assertFalse(fakeVibrator.getAllEffectSegments().stream() .anyMatch(PrebakedSegment.class::isInstance)); - cancelVibrate(service); // Clean up long effect. } @Test public void vibrate_withOngoingHigherImportanceVibration_ignoresEffect() throws Exception { mockVibrators(1); - mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorManagerService service = createSystemReadyService(); VibrationEffect effect = VibrationEffect.createWaveform( - new long[]{10_000, 10_000}, new int[]{128, 255}, -1); + new long[]{10, 10_000}, new int[]{128, 255}, -1); vibrate(service, effect, ALARM_ATTRS); - // VibrationThread will start this vibration async, so wait before checking it started. - assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), + // VibrationThread will start this vibration async. + // Wait until second step started to ensure the noteVibratorOn was triggered. + assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service, TEST_TIMEOUT_MILLIS)); vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), @@ -1078,26 +1096,27 @@ public class VibratorManagerServiceTest { // The second vibration shouldn't have recorded that the vibrators were turned on. verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong()); // The second vibration shouldn't have played any prebaked segment. - assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream() + assertFalse(fakeVibrator.getAllEffectSegments().stream() .anyMatch(PrebakedSegment.class::isInstance)); - cancelVibrate(service); // Clean up long effect. } @Test public void vibrate_withOngoingLowerImportanceVibration_cancelsOngoingEffect() throws Exception { mockVibrators(1); - mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); - mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); VibratorManagerService service = createSystemReadyService(); VibrationEffect effect = VibrationEffect.createWaveform( - new long[]{10_000, 10_000}, new int[]{128, 255}, -1); + new long[]{10, 10_000}, new int[]{128, 255}, -1); vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS); - // VibrationThread will start this vibration async, so wait before checking it started. - assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(), - service, TEST_TIMEOUT_MILLIS)); + // VibrationThread will start this vibration async. + // Wait until second step started to ensure the noteVibratorOn was triggered. + assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service, + TEST_TIMEOUT_MILLIS)); vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS); @@ -1105,10 +1124,8 @@ public class VibratorManagerServiceTest { // The second vibration should have recorded that the vibrators were turned on. verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong()); // One segment played is the prebaked CLICK from the second vibration. - assertEquals(1, - mVibratorProviders.get(1).getAllEffectSegments().stream() - .filter(PrebakedSegment.class::isInstance) - .count()); + assertEquals(1, fakeVibrator.getAllEffectSegments().stream() + .filter(PrebakedSegment.class::isInstance).count()); } @Test @@ -1139,10 +1156,8 @@ public class VibratorManagerServiceTest { // The new vibration should have recorded that the vibrators were turned on. verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong()); // One segment played is the prebaked CLICK from the new vibration. - assertEquals(1, - mVibratorProviders.get(1).getAllEffectSegments().stream() - .filter(PrebakedSegment.class::isInstance) - .count()); + assertEquals(1, mVibratorProviders.get(1).getAllEffectSegments().stream() + .filter(PrebakedSegment.class::isInstance).count()); } @Test @@ -1153,8 +1168,9 @@ public class VibratorManagerServiceTest { .build(); mockVibrators(1); - mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); - mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); VibratorManagerService service = createSystemReadyService(); VibrationEffect effect = VibrationEffect.createWaveform( @@ -1173,7 +1189,7 @@ public class VibratorManagerServiceTest { verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong()); // One step segment (with several amplitudes) and one click should have played. Notably // there is no primitive segment. - List<VibrationEffectSegment> played = mVibratorProviders.get(1).getAllEffectSegments(); + List<VibrationEffectSegment> played = fakeVibrator.getAllEffectSegments(); assertEquals(2, played.size()); assertEquals(1, played.stream().filter(StepSegment.class::isInstance).count()); assertEquals(1, played.stream().filter(PrebakedSegment.class::isInstance).count()); @@ -1182,8 +1198,9 @@ public class VibratorManagerServiceTest { @Test public void vibrate_withInputDevices_vibratesInputDevices() throws Exception { mockVibrators(1); - mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); - mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1}); when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1)); @@ -1198,7 +1215,7 @@ public class VibratorManagerServiceTest { vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS); verify(mIInputManagerMock).vibrateCombined(eq(1), eq(effect), any()); - assertTrue(mVibratorProviders.get(1).getAllEffectSegments().isEmpty()); + assertTrue(fakeVibrator.getAllEffectSegments().isEmpty()); } @Test @@ -1497,8 +1514,6 @@ public class VibratorManagerServiceTest { // Alarm vibration will be scaled with SCALE_NONE. assertEquals(1f, ((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(2)).getScale(), 1e-5); - - cancelVibrate(service); // Clean up long-ish effect. } @Test @@ -1533,8 +1548,6 @@ public class VibratorManagerServiceTest { assertEquals(1, fakeVibrator.getAllEffectSegments().size()); assertEquals(defaultAmplitude / 255f, fakeVibrator.getAmplitudes().get(0), 1e-5); - - cancelVibrate(service); // Clean up long-ish effect. } @Test @@ -1570,7 +1583,6 @@ public class VibratorManagerServiceTest { // Vibration is not stopped nearly after updating service. assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50)); - cancelVibrate(service); // Clean up long effect. } @Test @@ -1596,8 +1608,6 @@ public class VibratorManagerServiceTest { HAPTIC_FEEDBACK_ATTRS); // Haptic feedback played normally when it's from the default device. assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); - - cancelVibrate(service); // Clean up long-ish effect. } @Test @@ -1854,9 +1864,7 @@ public class VibratorManagerServiceTest { assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); // Vibration is not cancelled. - assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS)); assertEquals(Arrays.asList(false), mVibratorProviders.get(1).getExternalControlStates()); - cancelVibrate(service); // Clean up long effect. } @Test @@ -1910,9 +1918,7 @@ public class VibratorManagerServiceTest { assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); // Vibration is not cancelled. - assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS)); assertEquals(Arrays.asList(false), mVibratorProviders.get(1).getExternalControlStates()); - cancelVibrate(service); // Clean up long effect. } @Test @@ -2536,29 +2542,29 @@ public class VibratorManagerServiceTest { private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service, CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException { - HalVibration vib = - service.vibrateWithPermissionCheck(UID, Context.DEVICE_ID_DEFAULT, PACKAGE_NAME, - effect, attrs, "some reason", service); + HalVibration vib = vibrate(service, effect, attrs); if (vib != null) { vib.waitForEnd(); } - return vib; } - private void vibrate(VibratorManagerService service, VibrationEffect effect, + private HalVibration vibrate(VibratorManagerService service, VibrationEffect effect, VibrationAttributes attrs) { - vibrate(service, CombinedVibration.createParallel(effect), attrs); + return vibrate(service, CombinedVibration.createParallel(effect), attrs); } - private void vibrate(VibratorManagerService service, CombinedVibration effect, + private HalVibration vibrate(VibratorManagerService service, CombinedVibration effect, VibrationAttributes attrs) { - vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs); + return vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs); } - private void vibrateWithDevice(VibratorManagerService service, int deviceId, + private HalVibration vibrateWithDevice(VibratorManagerService service, int deviceId, CombinedVibration effect, VibrationAttributes attrs) { - service.vibrate(UID, deviceId, PACKAGE_NAME, effect, attrs, "some reason", service); + HalVibration vib = service.vibrateWithPermissionCheck(UID, deviceId, PACKAGE_NAME, effect, + attrs, "some reason", service); + mPendingVibrations.add(vib); + return vib; } private boolean waitUntil(Predicate<VibratorManagerService> predicate, diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java index 391220669f2f..2a010f0a82a9 100644 --- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java +++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java @@ -18,7 +18,10 @@ package com.android.server.vibrator; import android.annotation.NonNull; import android.frameworks.vibrator.IVibratorController; +import android.frameworks.vibrator.VibrationParam; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; import android.os.VibrationAttributes; @@ -28,17 +31,42 @@ import android.os.VibrationAttributes; */ public final class FakeVibratorController extends IVibratorController.Stub { + private final Handler mHandler; + private VibratorControlService mVibratorControlService; + private VibrationParam[] mRequestResult = new VibrationParam[0]; + public boolean isLinkedToDeath = false; public boolean didRequestVibrationParams = false; public int requestVibrationType = VibrationAttributes.USAGE_UNKNOWN; public long requestTimeoutInMillis = 0; + public FakeVibratorController(Looper looper) { + mHandler = new Handler(looper); + } + + public void setVibratorControlService(VibratorControlService service) { + mVibratorControlService = service; + } + + public void setRequestResult(VibrationParam... params) { + mRequestResult = params; + } + @Override - public void requestVibrationParams(int vibrationType, long timeoutInMillis, IBinder iBinder) + public void requestVibrationParams(int vibrationType, long timeoutInMillis, IBinder token) throws RemoteException { didRequestVibrationParams = true; requestVibrationType = vibrationType; requestTimeoutInMillis = timeoutInMillis; + mHandler.post(() -> { + if (mVibratorControlService != null) { + try { + mVibratorControlService.onRequestVibrationParamsComplete(token, mRequestResult); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + }); } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index a0e64bf94393..12f46df451fe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -82,7 +82,10 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.ArraySet; import android.util.MergedConfiguration; import android.view.ContentRecordingSession; @@ -109,6 +112,7 @@ import com.android.internal.os.IResultReceiver; import com.android.server.LocalServices; import com.android.server.wm.SensitiveContentPackages.PackageInfo; import com.android.server.wm.WindowManagerService.WindowContainerInfo; +import com.android.window.flags.Flags; import com.google.common.truth.Expect; @@ -140,6 +144,9 @@ public class WindowManagerServiceTests extends WindowTestsBase { @Rule public Expect mExpect = Expect.create(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @After public void tearDown() { mWm.mSensitiveContentPackages.clearBlockedApps(); @@ -1106,6 +1113,7 @@ public class WindowManagerServiceTests extends WindowTestsBase { argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY)); } + @RequiresFlagsDisabled(Flags.FLAG_MAGNIFICATION_ALWAYS_DRAW_FULLSCREEN_BORDER) @Test public void testDrawMagnifiedViewport() { final int displayId = mDisplayContent.mDisplayId; |