Merge "Add some more scenes, replace linear with uniform" into sc-dev
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 1bd90a8..cfcb4e7 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimPriv.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index 544bca02..0948e47 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/CtsShim.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 72386bb..db64475 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimPriv.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index 893eac2..80812df 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShim.apk"
}
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index afd8e29..ac63653 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -144,7 +144,7 @@
while (state.keepRunning()) {
state.pauseTiming();
// Invalidate cache.
- resourcesManager.applyConfigurationToResourcesLocked(
+ resourcesManager.applyConfigurationToResources(
resourcesManager.getConfiguration(), null);
state.resumeTiming();
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
index aca0fc0..f0b04fc 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
@@ -19,8 +19,6 @@
import android.annotation.Nullable;
import android.util.ArrayMap;
-import com.android.internal.util.Preconditions;
-
import java.util.Map;
import java.util.Objects;
@@ -117,30 +115,26 @@
/**
* Builder for {@link AppSearchBatchResult} objects.
*
- * <p>Once {@link #build} is called, the instance can no longer be used.
- *
* @param <KeyType> The type of the keys for which the results will be reported.
* @param <ValueType> The type of the result objects for successful results.
*/
public static final class Builder<KeyType, ValueType> {
- private final Map<KeyType, ValueType> mSuccesses = new ArrayMap<>();
- private final Map<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
- private final Map<KeyType, AppSearchResult<ValueType>> mAll = new ArrayMap<>();
+ private ArrayMap<KeyType, ValueType> mSuccesses = new ArrayMap<>();
+ private ArrayMap<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
+ private ArrayMap<KeyType, AppSearchResult<ValueType>> mAll = new ArrayMap<>();
private boolean mBuilt = false;
/**
* Associates the {@code key} with the provided successful return value.
*
* <p>Any previous mapping for a key, whether success or failure, is deleted.
- *
- * @throws IllegalStateException if the builder has already been used.
*/
@SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
@NonNull
public Builder<KeyType, ValueType> setSuccess(
@NonNull KeyType key, @Nullable ValueType result) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(key);
+ resetIfBuilt();
return setResult(key, AppSearchResult.newSuccessfulResult(result));
}
@@ -148,8 +142,6 @@
* Associates the {@code key} with the provided failure code and error message.
*
* <p>Any previous mapping for a key, whether success or failure, is deleted.
- *
- * @throws IllegalStateException if the builder has already been used.
*/
@SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
@NonNull
@@ -157,8 +149,8 @@
@NonNull KeyType key,
@AppSearchResult.ResultCode int resultCode,
@Nullable String errorMessage) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(key);
+ resetIfBuilt();
return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage));
}
@@ -166,16 +158,14 @@
* Associates the {@code key} with the provided {@code result}.
*
* <p>Any previous mapping for a key, whether success or failure, is deleted.
- *
- * @throws IllegalStateException if the builder has already been used.
*/
@SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
@NonNull
public Builder<KeyType, ValueType> setResult(
@NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(key);
Objects.requireNonNull(result);
+ resetIfBuilt();
if (result.isSuccess()) {
mSuccesses.put(key, result.getResultValue());
mFailures.remove(key);
@@ -189,14 +179,21 @@
/**
* Builds an {@link AppSearchBatchResult} object from the contents of this {@link Builder}.
- *
- * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public AppSearchBatchResult<KeyType, ValueType> build() {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
mBuilt = true;
- return new AppSearchBatchResult<>(mSuccesses, mFailures, mAll);
+ return new AppSearchBatchResult<>(
+ new ArrayMap<>(mSuccesses), new ArrayMap<>(mFailures), new ArrayMap<>(mAll));
+ }
+
+ private void resetIfBuilt() {
+ if (mBuilt) {
+ mSuccesses = new ArrayMap<>(mSuccesses);
+ mFailures = new ArrayMap<>(mFailures);
+ mAll = new ArrayMap<>(mAll);
+ mBuilt = false;
+ }
}
}
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 736deab..cc48ccb 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -892,129 +892,121 @@
@Override
@NonNull
public String toString() {
- return formatGenericDocumentString(this, /*indentLevel=*/ 0);
+ StringBuilder stringBuilder = new StringBuilder();
+ appendGenericDocumentString(this, /*indentLevel=*/ 0, stringBuilder);
+ return stringBuilder.toString();
}
- @NonNull
- private static String formatGenericDocumentString(
- @NonNull GenericDocument document, int indentLevel) {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append(getIndent(indentLevel)).append("{\n");
+ private static void appendGenericDocumentString(
+ @NonNull GenericDocument document, int indentLevel, @NonNull StringBuilder builder) {
+ Objects.requireNonNull(document);
+ Objects.requireNonNull(builder);
- String indentLevelOneString = getIndent(indentLevel + 1);
+ builder.append(getIndent(indentLevel)).append("{\n");
- stringBuilder
- .append(indentLevelOneString)
+ String indent1 = getIndent(indentLevel + 1);
+
+ builder.append(indent1)
.append("namespace: \"")
.append(document.getNamespace())
.append("\",\n");
- stringBuilder
- .append(indentLevelOneString)
- .append("id: \"")
- .append(document.getId())
- .append("\",\n");
+ builder.append(indent1).append("id: \"").append(document.getId()).append("\",\n");
- stringBuilder
- .append(indentLevelOneString)
- .append("score: " + document.getScore())
- .append(",\n");
+ builder.append(indent1).append("score: ").append(document.getScore()).append(",\n");
- stringBuilder
- .append(indentLevelOneString)
+ builder.append(indent1)
.append("schemaType: \"")
.append(document.getSchemaType())
.append("\",\n");
- stringBuilder
- .append(indentLevelOneString)
- .append("creationTimestampMillis: " + document.getCreationTimestampMillis())
+ builder.append(indent1)
+ .append("creationTimestampMillis: ")
+ .append(document.getCreationTimestampMillis())
.append(",\n");
- stringBuilder
- .append(indentLevelOneString)
- .append("timeToLiveMillis: " + document.getTtlMillis())
+ builder.append(indent1)
+ .append("timeToLiveMillis: ")
+ .append(document.getTtlMillis())
.append(",\n");
- stringBuilder.append(indentLevelOneString).append("properties: {\n");
+ builder.append(indent1).append("properties: {\n");
- int idx = 0;
- for (String propertyName : document.getPropertyNames()) {
- Object property = document.getProperty(propertyName);
- stringBuilder
- .append(getIndent(indentLevel + 2))
+ String[] sortedProperties = document.getPropertyNames().toArray(new String[0]);
+ Arrays.sort(sortedProperties);
+
+ for (int i = 0; i < sortedProperties.length; i++) {
+ Object property = document.getProperty(sortedProperties[i]);
+ builder.append(getIndent(indentLevel + 2))
.append("\"")
- .append(propertyName)
+ .append(sortedProperties[i])
.append("\"")
.append(": ");
- stringBuilder.append(getPropertyString(property, indentLevel + 2));
- if (idx != document.getPropertyNames().size() - 1) {
- stringBuilder.append(",\n");
+ appendPropertyString(property, indentLevel + 2, builder);
+ if (i != sortedProperties.length - 1) {
+ builder.append(",\n");
}
- ++idx;
}
- stringBuilder.append("\n");
- stringBuilder.append(indentLevelOneString).append("}");
+ builder.append("\n");
+ builder.append(indent1).append("}");
- stringBuilder.append("\n");
- stringBuilder.append(getIndent(indentLevel)).append("}");
-
- return stringBuilder.toString();
+ builder.append("\n");
+ builder.append(getIndent(indentLevel)).append("}");
}
/**
- * Creates string for property.
+ * Appends a string for the given property to the given builder.
*
* @param property property object to create string for.
* @param indentLevel base indent level for property.
+ * @param builder the builder to append to.
*/
- @NonNull
- private static String getPropertyString(@NonNull Object property, int indentLevel) {
+ private static void appendPropertyString(
+ @NonNull Object property, int indentLevel, @NonNull StringBuilder builder) {
Objects.requireNonNull(property);
+ Objects.requireNonNull(builder);
- StringBuilder str = new StringBuilder("[");
-
+ builder.append("[");
if (property instanceof GenericDocument[]) {
GenericDocument[] documentValues = (GenericDocument[]) property;
for (int i = 0; i < documentValues.length; ++i) {
- str.append("\n");
- str.append(formatGenericDocumentString(documentValues[i], indentLevel + 1));
+ builder.append("\n");
+ appendGenericDocumentString(documentValues[i], indentLevel + 1, builder);
if (i != documentValues.length - 1) {
- str.append(", ");
+ builder.append(", ");
}
- str.append("\n");
+ builder.append("\n");
}
- str.append(getIndent(indentLevel));
+ builder.append(getIndent(indentLevel));
} else {
int propertyArrLength = Array.getLength(property);
for (int i = 0; i < propertyArrLength; i++) {
Object propertyElement = Array.get(property, i);
if (propertyElement instanceof String) {
- str.append("\"").append(propertyElement).append("\"");
+ builder.append("\"").append(propertyElement).append("\"");
} else if (propertyElement instanceof byte[]) {
- str.append(Arrays.toString((byte[]) propertyElement));
+ builder.append(Arrays.toString((byte[]) propertyElement));
} else {
- str.append(propertyElement);
+ builder.append(propertyElement);
}
if (i != propertyArrLength - 1) {
- str.append(", ");
+ builder.append(", ");
}
}
}
- str.append("]");
- return str.toString();
+ builder.append("]");
}
- /** Creates string for given indent level. */
+ /** Appends a string for given indent level to the given builder. */
@NonNull
private static String getIndent(int indentLevel) {
- StringBuilder indentedString = new StringBuilder();
+ StringBuilder builder = new StringBuilder();
for (int i = 0; i < indentLevel; ++i) {
- indentedString.append(" ");
+ builder.append(" ");
}
- return indentedString.toString();
+ return builder.toString();
}
/**
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index b6b7fde..777f9fe 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -234,6 +234,18 @@
}
}
+ @Override
+ public void onUserStopping(@NonNull TargetUser user) {
+ synchronized (mUnlockedUserIdsLocked) {
+ mUnlockedUserIdsLocked.remove(user.getUserIdentifier());
+ try {
+ mImplInstanceManager.closeAndRemoveAppSearchImplForUser(user.getUserIdentifier());
+ } catch (Throwable t) {
+ Log.e(TAG, "Error handling user stopping.", t);
+ }
+ }
+ }
+
private void verifyUserUnlocked(int callingUserId) {
if (isUserLocked(callingUserId)) {
throw new IllegalStateException("User " + callingUserId + " is locked or not running.");
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index 94ee830..b815de4 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -117,11 +117,29 @@
*/
public void removeAppSearchImplForUser(@UserIdInt int userId) {
synchronized (mInstancesLocked) {
+ // no need to close and persist data to disk since we are removing them now.
mInstancesLocked.remove(userId);
}
}
/**
+ * Close and remove an instance of {@link AppSearchImpl} for the given user.
+ *
+ * <p>All mutation apply to this {@link AppSearchImpl} will be persisted to disk.
+ *
+ * @param userId The multi-user userId of the user that need to be removed.
+ */
+ public void closeAndRemoveAppSearchImplForUser(@UserIdInt int userId) {
+ synchronized (mInstancesLocked) {
+ AppSearchImpl appSearchImpl = mInstancesLocked.get(userId);
+ if (appSearchImpl != null) {
+ appSearchImpl.close();
+ mInstancesLocked.remove(userId);
+ }
+ }
+ }
+
+ /**
* Gets an instance of AppSearchImpl for the given user.
*
* <p>This method should only be called by an initialized SearchSession, which has been already
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
index ecc774c..6b443b3 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
@@ -128,14 +128,14 @@
return new SearchResult.MatchInfo.Builder(propertyPath)
.setExactMatchRange(
new SearchResult.MatchRange(
- snippetMatchProto.getExactMatchPosition(),
- snippetMatchProto.getExactMatchPosition()
- + snippetMatchProto.getExactMatchBytes()))
+ snippetMatchProto.getExactMatchUtf16Position(),
+ snippetMatchProto.getExactMatchUtf16Position()
+ + snippetMatchProto.getExactMatchUtf16Length()))
.setSnippetRange(
new SearchResult.MatchRange(
- snippetMatchProto.getWindowPosition(),
- snippetMatchProto.getWindowPosition()
- + snippetMatchProto.getWindowBytes()))
+ snippetMatchProto.getWindowUtf16Position(),
+ snippetMatchProto.getWindowUtf16Position()
+ + snippetMatchProto.getWindowUtf16Length()))
.build();
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
index cf640c1..ea5263a 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
@@ -43,13 +43,16 @@
CALL_TYPE_SET_SCHEMA,
CALL_TYPE_PUT_DOCUMENTS,
CALL_TYPE_GET_DOCUMENTS,
- CALL_TYPE_REMOVE_DOCUMENTS,
+ CALL_TYPE_REMOVE_DOCUMENTS_BY_ID,
CALL_TYPE_PUT_DOCUMENT,
CALL_TYPE_GET_DOCUMENT,
- CALL_TYPE_REMOVE_DOCUMENT,
- CALL_TYPE_QUERY,
+ CALL_TYPE_REMOVE_DOCUMENT_BY_ID,
+ CALL_TYPE_SEARCH,
CALL_TYPE_OPTIMIZE,
CALL_TYPE_FLUSH,
+ CALL_TYPE_GLOBAL_SEARCH,
+ CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH,
+ CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CallType {}
@@ -59,13 +62,16 @@
public static final int CALL_TYPE_SET_SCHEMA = 2;
public static final int CALL_TYPE_PUT_DOCUMENTS = 3;
public static final int CALL_TYPE_GET_DOCUMENTS = 4;
- public static final int CALL_TYPE_REMOVE_DOCUMENTS = 5;
+ public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_ID = 5;
public static final int CALL_TYPE_PUT_DOCUMENT = 6;
public static final int CALL_TYPE_GET_DOCUMENT = 7;
- public static final int CALL_TYPE_REMOVE_DOCUMENT = 8;
- public static final int CALL_TYPE_QUERY = 9;
+ public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_ID = 8;
+ public static final int CALL_TYPE_SEARCH = 9;
public static final int CALL_TYPE_OPTIMIZE = 10;
public static final int CALL_TYPE_FLUSH = 11;
+ public static final int CALL_TYPE_GLOBAL_SEARCH = 12;
+ public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH = 13;
+ public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH = 14;
@NonNull private final GeneralStats mGeneralStats;
@CallType private final int mCallType;
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index d07a3b9..85d85aa 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ibbf4260deb720ce724be81ee4394ea96181ee0f7
+I0216abecc41d020f16ed8947a9f37b710afd331e
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
index 941cea9..71b4f36 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
@@ -17,6 +17,7 @@
package com.android.server.appsearch.testing;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
@@ -37,6 +38,7 @@
import android.app.appsearch.StorageInfo;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
+import android.os.UserHandle;
import androidx.test.core.app.ApplicationProvider;
@@ -58,18 +60,29 @@
private final AppSearchSession mAppSearchSession;
private final ExecutorService mExecutor;
+ /** Creates the SearchSessionShim with given SearchContext. */
@NonNull
public static ListenableFuture<AppSearchSessionShim> createSearchSession(
@NonNull AppSearchManager.SearchContext searchContext) {
- return createSearchSession(searchContext, Executors.newCachedThreadPool());
+ Context context = ApplicationProvider.getApplicationContext();
+ return createSearchSession(context, searchContext, Executors.newCachedThreadPool());
}
- /** Creates the SearchSession with given ExecutorService. */
+ /** Creates the SearchSessionShim with given SearchContext for the given user. */
@NonNull
public static ListenableFuture<AppSearchSessionShim> createSearchSession(
+ @NonNull AppSearchManager.SearchContext searchContext, @UserIdInt int userId) {
+ Context context = ApplicationProvider.getApplicationContext()
+ .createContextAsUser(new UserHandle(userId), /*flags=*/ 0);
+ return createSearchSession(context, searchContext, Executors.newCachedThreadPool());
+ }
+
+ /** Creates the SearchSession with given Context and ExecutorService. */
+ @NonNull
+ public static ListenableFuture<AppSearchSessionShim> createSearchSession(
+ @NonNull Context context,
@NonNull AppSearchManager.SearchContext searchContext,
@NonNull ExecutorService executor) {
- Context context = ApplicationProvider.getApplicationContext();
AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
SettableFuture<AppSearchResult<AppSearchSession>> future = SettableFuture.create();
appSearchManager.createSearchSession(searchContext, executor, future::set);
diff --git a/core/api/current.txt b/core/api/current.txt
index 324ad33..98c2d40 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11154,6 +11154,7 @@
field public static final String ACTION_VIEW = "android.intent.action.VIEW";
field public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE = "android.intent.action.VIEW_PERMISSION_USAGE";
+ field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD = "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
field public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
field @Deprecated public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
field public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
@@ -11206,6 +11207,7 @@
field public static final String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
field public static final String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
field public static final String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+ field public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
field public static final String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE = "android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
field public static final String EXTRA_BCC = "android.intent.extra.BCC";
field public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
@@ -11231,6 +11233,7 @@
field public static final String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
field public static final String EXTRA_DURATION_MILLIS = "android.intent.extra.DURATION_MILLIS";
field public static final String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+ field public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
field public static final String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
field public static final String EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE";
field public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
@@ -11245,6 +11248,7 @@
field public static final String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
field public static final String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
field public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+ field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
field public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
field public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
field public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -11267,6 +11271,7 @@
field @Deprecated public static final String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
field public static final String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
field public static final String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
+ field public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
field public static final String EXTRA_STREAM = "android.intent.extra.STREAM";
field public static final String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
field public static final String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS";
@@ -13079,7 +13084,6 @@
method public void reportShortcutUsed(String);
method @WorkerThread public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender);
method @WorkerThread public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
- method public void updateShortcutVisibility(@NonNull String, @Nullable byte[], boolean);
method @WorkerThread public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
field public static final int FLAG_MATCH_CACHED = 8; // 0x8
field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
@@ -46814,6 +46818,11 @@
method public void onActionProviderVisibilityChanged(boolean);
}
+ @UiThread public interface AttachedSurfaceControl {
+ method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction);
+ method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
+ }
+
public final class Choreographer {
method public static android.view.Choreographer getInstance();
method public void postFrameCallback(android.view.Choreographer.FrameCallback);
@@ -48540,6 +48549,7 @@
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
+ method public void dispatchCreateViewTranslationRequest(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @NonNull android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
method public void dispatchDisplayHint(int);
method public boolean dispatchDragEvent(android.view.DragEvent);
method protected void dispatchDraw(android.graphics.Canvas);
@@ -48561,7 +48571,6 @@
method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void dispatchProvideAutofillStructure(@NonNull android.view.ViewStructure, int);
method public void dispatchProvideStructure(android.view.ViewStructure);
- method public void dispatchRequestTranslation(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @NonNull android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
method public void dispatchScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>);
@@ -48714,6 +48723,7 @@
method public final int getRight();
method protected float getRightFadingEdgeStrength();
method protected int getRightPaddingOffset();
+ method @Nullable public android.view.AttachedSurfaceControl getRootSurfaceControl();
method public android.view.View getRootView();
method public android.view.WindowInsets getRootWindowInsets();
method public float getRotation();
@@ -48758,7 +48768,6 @@
method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarThumbDrawable();
method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarTrackDrawable();
method public int getVerticalScrollbarWidth();
- method @Nullable public android.view.ViewRoot getViewRoot();
method @Nullable public android.view.translation.ViewTranslationResponse getViewTranslationResponse();
method public android.view.ViewTreeObserver getViewTreeObserver();
method public int getVisibility();
@@ -49834,11 +49843,6 @@
method public android.view.ViewPropertyAnimator zBy(float);
}
- @UiThread public interface ViewRoot {
- method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction);
- method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
- }
-
public abstract class ViewStructure {
ctor public ViewStructure();
method public abstract int addChildCount(int);
@@ -50025,6 +50029,7 @@
method @ColorInt public int getNavigationBarDividerColor();
method public android.transition.Transition getReenterTransition();
method public android.transition.Transition getReturnTransition();
+ method @Nullable public android.view.AttachedSurfaceControl getRootSurfaceControl();
method public android.transition.Transition getSharedElementEnterTransition();
method public android.transition.Transition getSharedElementExitTransition();
method public android.transition.Transition getSharedElementReenterTransition();
@@ -50034,7 +50039,6 @@
method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects();
method public long getTransitionBackgroundFadeDuration();
method public android.transition.TransitionManager getTransitionManager();
- method @Nullable public android.view.ViewRoot getViewRoot();
method public abstract int getVolumeControlStream();
method public android.view.WindowManager getWindowManager();
method public final android.content.res.TypedArray getWindowStyle();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 96a23b2..62ffac7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2399,12 +2399,9 @@
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
- field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD = "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
- field public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
- field public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
field public static final String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET";
field public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
field public static final String EXTRA_INSTANT_APP_BUNDLES = "android.intent.extra.INSTANT_APP_BUNDLES";
@@ -2416,13 +2413,11 @@
field public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
field public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
field public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
- field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
field public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
field public static final String EXTRA_REASON = "android.intent.extra.REASON";
field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
- field public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000
@@ -14919,6 +14914,7 @@
public static interface WebViewProvider.ViewDelegate {
method public default void autofill(android.util.SparseArray<android.view.autofill.AutofillValue>);
+ method public default void dispatchCreateViewTranslationRequest(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @Nullable android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
method public boolean dispatchKeyEvent(android.view.KeyEvent);
method public android.view.View findFocus(android.view.View);
method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f458107..c8e365e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -865,6 +865,10 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ProviderInfoList> CREATOR;
}
+ public final class SharedLibraryInfo implements android.os.Parcelable {
+ method @NonNull public java.util.List<java.lang.String> getAllCodePaths();
+ }
+
public final class ShortcutInfo implements android.os.Parcelable {
method public boolean isVisibleToPublisher();
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ff210e1..a667767 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5887,7 +5887,7 @@
public final void applyConfigurationToResources(Configuration config) {
synchronized (mResourcesManager) {
- mResourcesManager.applyConfigurationToResourcesLocked(config, null);
+ mResourcesManager.applyConfigurationToResources(config, null);
}
}
@@ -5975,7 +5975,7 @@
synchronized (mResourcesManager) {
// Update all affected Resources objects to use new ResourcesImpl
- mResourcesManager.applyNewResourceDirsLocked(ai, oldResDirs);
+ mResourcesManager.applyNewResourceDirs(ai, oldResDirs);
}
}
@@ -6231,7 +6231,7 @@
synchronized (mResourcesManager) {
// Update affected Resources objects to use new ResourcesImpl
- mResourcesManager.applyNewResourceDirsLocked(aInfo, oldResDirs);
+ mResourcesManager.applyNewResourceDirs(aInfo, oldResDirs);
}
} catch (RemoteException e) {
}
@@ -6474,7 +6474,7 @@
* reflect configuration changes. The configuration object passed
* in AppBindData can be safely assumed to be up to date
*/
- mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+ mResourcesManager.applyConfigurationToResources(data.config, data.compatInfo);
mCurDefaultDisplayDpi = data.config.densityDpi;
// This calls mResourcesManager so keep it within the synchronized block.
@@ -7509,7 +7509,7 @@
// We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it.
- if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
+ if (mResourcesManager.applyConfigurationToResources(globalConfig,
null /* compat */,
mInitialApplication.getResources().getDisplayAdjustments())) {
mConfigurationController.updateLocaleListFromAppContext(
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 0dbe3ba..6d92201 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -107,8 +107,7 @@
mCompatConfiguration = new Configuration();
}
mCompatConfiguration.setTo(mConfiguration);
- if (mResourcesManager.applyCompatConfigurationLocked(displayDensity,
- mCompatConfiguration)) {
+ if (mResourcesManager.applyCompatConfiguration(displayDensity, mCompatConfiguration)) {
config = mCompatConfiguration;
}
return config;
@@ -199,7 +198,7 @@
// configuration also needs to set to the adjustments for consistency.
appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
}
- mResourcesManager.applyConfigurationToResourcesLocked(config, compat,
+ mResourcesManager.applyConfigurationToResources(config, compat,
appResources.getDisplayAdjustments());
updateLocaleListFromAppContext(app.getApplicationContext());
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2dcdd07..c6847aa 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5662,11 +5662,6 @@
R.dimen.call_notification_collapsible_indent);
}
big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
- if (p.mCallStyleActions) {
- // Use "wrap_content" (unlike normal emphasized mode) and allow prioritizing the
- // required actions (Answer, Decline, and Hang Up).
- big.setBoolean(R.id.actions, "setPrioritizedWrapMode", true);
- }
if (numActions > 0 && !p.mHideActions) {
big.setViewVisibility(R.id.actions_container, View.VISIBLE);
big.setViewVisibility(R.id.actions, View.VISIBLE);
@@ -5683,7 +5678,7 @@
// Clear the drawable
button.setInt(R.id.action0, "setBackgroundResource", 0);
}
- if (p.mCallStyleActions && i > 0) {
+ if (emphazisedMode && i > 0) {
// Clear start margin from non-first buttons to reduce the gap between them.
// (8dp remaining gap is from all buttons' standard 4dp inset).
button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
@@ -6103,26 +6098,21 @@
// change the background bgColor
CharSequence title = action.title;
ColorStateList[] outResultColor = new ColorStateList[1];
- int background = getBackgroundColor(p);
+ int background = getSecondaryAccentColor(p);
if (isLegacy()) {
title = ContrastColorUtil.clearColorSpans(title);
} else {
title = ensureColorSpanContrast(title, background, outResultColor);
}
button.setTextViewText(R.id.action0, processTextSpans(title));
- final int textColor;
boolean hasColorOverride = outResultColor[0] != null;
if (hasColorOverride) {
// There's a span spanning the full text, let's take it and use it as the
// background color
background = outResultColor[0].getDefaultColor();
- textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
- background, mInNightMode);
- } else if (mTintActionButtons && !mInNightMode && !isBackgroundColorized(p)) {
- textColor = getAccentColor(p);
- } else {
- textColor = getPrimaryTextColor(p);
}
+ final int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
+ background, mInNightMode);
button.setTextColor(R.id.action0, textColor);
// We only want about 20% alpha for the ripple
final int rippleColor = (textColor & 0x00ffffff) | 0x33000000;
@@ -6130,11 +6120,10 @@
ColorStateList.valueOf(rippleColor));
button.setColorStateList(R.id.action0, "setButtonBackground",
ColorStateList.valueOf(background));
- button.setBoolean(R.id.action0, "setHasStroke", !hasColorOverride);
if (p.mCallStyleActions) {
button.setImageViewIcon(R.id.action0, action.getIcon());
boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
- button.setBoolean(R.id.action0, "setWrapModePriority", priority);
+ button.setBoolean(R.id.action0, "setIsPriority", priority);
int minWidthDimen =
priority ? R.dimen.call_notification_system_action_min_width : 0;
button.setIntDimen(R.id.action0, "setMinimumWidth", minWidthDimen);
@@ -6320,6 +6309,22 @@
}
/**
+ * Gets the secondary accent color for colored UI elements. If we're tinting with the theme
+ * accent, this is the theme accent color, otherwise this would be identical to
+ * {@link #getSmallIconColor(StandardTemplateParams)}.
+ */
+ private @ColorInt int getSecondaryAccentColor(StandardTemplateParams p) {
+ if (isBackgroundColorized(p)) {
+ return getSecondaryTextColor(p);
+ }
+ int color = obtainThemeColor(R.attr.colorAccentSecondary, COLOR_INVALID);
+ if (color != COLOR_INVALID) {
+ return color;
+ }
+ return getContrastColor(p);
+ }
+
+ /**
* Gets the "surface protection" color from the theme, or a variant of the normal background
* color when colorized, or when not using theme color tints.
*/
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 74134e1..792336d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -75,6 +75,11 @@
private static ResourcesManager sResourcesManager;
/**
+ * Internal lock object
+ */
+ private final Object mLock = new Object();
+
+ /**
* The global compatibility settings.
*/
private CompatibilityInfo mResCompatibilityInfo;
@@ -275,7 +280,7 @@
* try as hard as possible to release any open FDs.
*/
public void invalidatePath(String path) {
- synchronized (this) {
+ synchronized (mLock) {
int count = 0;
for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
@@ -304,7 +309,7 @@
}
public Configuration getConfiguration() {
- synchronized (this) {
+ synchronized (mLock) {
return mResConfiguration;
}
}
@@ -351,13 +356,15 @@
config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
}
- public boolean applyCompatConfigurationLocked(int displayDensity,
+ public boolean applyCompatConfiguration(int displayDensity,
@NonNull Configuration compatConfiguration) {
- if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
- mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
- return true;
+ synchronized (mLock) {
+ if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
+ mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
+ return true;
+ }
+ return false;
}
- return false;
}
/**
@@ -376,7 +383,7 @@
final Pair<Integer, DisplayAdjustments> key =
Pair.create(displayId, displayAdjustmentsCopy);
SoftReference<Display> sd;
- synchronized (this) {
+ synchronized (mLock) {
sd = mAdjustedDisplays.get(key);
}
if (sd != null) {
@@ -392,7 +399,7 @@
}
final Display display = dm.getCompatibleDisplay(displayId, key.second);
if (display != null) {
- synchronized (this) {
+ synchronized (mLock) {
mAdjustedDisplays.put(key, new SoftReference<>(display));
}
}
@@ -407,7 +414,7 @@
* @param resources The {@link Resources} backing the display adjustments.
*/
public Display getAdjustedDisplay(final int displayId, Resources resources) {
- synchronized (this) {
+ synchronized (mLock) {
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
if (dm == null) {
// may be null early in system startup
@@ -425,7 +432,7 @@
ApkAssets apkAssets;
// Optimistically check if this ApkAssets exists somewhere else.
- synchronized (this) {
+ synchronized (mLock) {
final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(key);
if (apkAssetsRef != null) {
apkAssets = apkAssetsRef.get();
@@ -447,7 +454,7 @@
key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
}
- synchronized (this) {
+ synchronized (mLock) {
mCachedApkAssets.put(key, new WeakReference<>(apkAssets));
}
@@ -559,7 +566,7 @@
* @hide
*/
public void dump(String prefix, PrintWriter printWriter) {
- synchronized (this) {
+ synchronized (mLock) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
for (int i = 0; i < prefix.length() / 2; i++) {
pw.increaseIndent();
@@ -688,7 +695,7 @@
*/
boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
@Nullable Configuration overrideConfig) {
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources activityResources
= activityToken != null ? mActivityResourceReferences.get(activityToken) : null;
if (activityResources == null) {
@@ -834,7 +841,7 @@
+ " with key=" + key);
}
- synchronized (this) {
+ synchronized (mLock) {
// Force the creation of an ActivityResourcesStruct.
getOrCreateActivityResourcesStructLocked(token);
}
@@ -842,7 +849,7 @@
// Update any existing Activity Resources references.
updateResourcesForActivity(token, overrideConfig, displayId);
- synchronized (this) {
+ synchronized (mLock) {
Resources resources = findResourcesForActivityLocked(token, key,
classLoader);
if (resources != null) {
@@ -868,7 +875,7 @@
*/
private void rebaseKeyForActivity(IBinder activityToken, ResourcesKey key,
boolean overridesActivityDisplay) {
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
@@ -960,7 +967,7 @@
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#createApkAssetsSupplierNotLocked");
try {
- if (DEBUG && Thread.holdsLock(this)) {
+ if (DEBUG && Thread.holdsLock(mLock)) {
Slog.w(TAG, "Calling thread " + Thread.currentThread().getName()
+ " is holding mLock", new Throwable());
}
@@ -994,7 +1001,7 @@
@Nullable
private Resources createResources(@NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
- synchronized (this) {
+ synchronized (mLock) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
@@ -1015,7 +1022,7 @@
@NonNull ResourcesKey key, @NonNull Configuration initialOverrideConfig,
@Nullable Integer overrideDisplayId, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
- synchronized (this) {
+ synchronized (mLock) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
@@ -1130,7 +1137,7 @@
if (displayId == INVALID_DISPLAY) {
throw new IllegalArgumentException("displayId can not be INVALID_DISPLAY");
}
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
@@ -1269,67 +1276,64 @@
public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
- synchronized(this) {
- return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
- }
- }
-
- public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
- @Nullable CompatibilityInfo compat) {
- return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
+ return applyConfigurationToResources(config, compat, null /* adjustments */);
}
/** Applies the global configuration to the managed resources. */
- public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
+ public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) {
- try {
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
- "ResourcesManager#applyConfigurationToResourcesLocked");
+ synchronized (mLock) {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#applyConfigurationToResources");
- if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
- if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
- + mResConfiguration.seq + ", newSeq=" + config.seq);
- return false;
- }
-
- // Things might have changed in display manager, so clear the cached displays.
- mAdjustedDisplays.clear();
-
- int changes = mResConfiguration.updateFrom(config);
- if (compat != null && (mResCompatibilityInfo == null ||
- !mResCompatibilityInfo.equals(compat))) {
- mResCompatibilityInfo = compat;
- changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
- | ActivityInfo.CONFIG_SCREEN_SIZE
- | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
- }
-
- DisplayMetrics displayMetrics = getDisplayMetrics();
- if (adjustments != null) {
- // Currently the only case where the adjustment takes effect is to simulate placing
- // an app in a rotated display.
- adjustments.adjustGlobalAppMetrics(displayMetrics);
- }
- Resources.updateSystemConfiguration(config, displayMetrics, compat);
-
- ApplicationPackageManager.configurationChanged();
-
- Configuration tmpConfig = new Configuration();
-
- for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
- ResourcesKey key = mResourceImpls.keyAt(i);
- WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
- ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
- if (r != null) {
- applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
- } else {
- mResourceImpls.removeAt(i);
+ if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+ if (DEBUG || DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Skipping new config: curSeq="
+ + mResConfiguration.seq + ", newSeq=" + config.seq);
+ }
+ return false;
}
- }
- return changes != 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ // Things might have changed in display manager, so clear the cached displays.
+ mAdjustedDisplays.clear();
+
+ int changes = mResConfiguration.updateFrom(config);
+ if (compat != null && (mResCompatibilityInfo == null
+ || !mResCompatibilityInfo.equals(compat))) {
+ mResCompatibilityInfo = compat;
+ changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+ | ActivityInfo.CONFIG_SCREEN_SIZE
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+ }
+
+ DisplayMetrics displayMetrics = getDisplayMetrics();
+ if (adjustments != null) {
+ // Currently the only case where the adjustment takes effect is to simulate
+ // placing an app in a rotated display.
+ adjustments.adjustGlobalAppMetrics(displayMetrics);
+ }
+ Resources.updateSystemConfiguration(config, displayMetrics, compat);
+
+ ApplicationPackageManager.configurationChanged();
+
+ Configuration tmpConfig = new Configuration();
+
+ for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
+ ResourcesKey key = mResourceImpls.keyAt(i);
+ WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
+ if (r != null) {
+ applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
+ } else {
+ mResourceImpls.removeAt(i);
+ }
+ }
+
+ return changes != 0;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
}
}
@@ -1378,7 +1382,7 @@
* @param libAssets The library asset paths to add.
*/
public void appendLibAssetsForMainAssetPath(String assetPath, String[] libAssets) {
- synchronized (this) {
+ synchronized (mLock) {
// Record which ResourcesImpl need updating
// (and what ResourcesKey they should update to).
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
@@ -1414,54 +1418,56 @@
}
// TODO(adamlesinski): Make this accept more than just overlay directories.
- final void applyNewResourceDirsLocked(@NonNull final ApplicationInfo appInfo,
+ void applyNewResourceDirs(@NonNull final ApplicationInfo appInfo,
@Nullable final String[] oldPaths) {
- try {
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
- "ResourcesManager#applyNewResourceDirsLocked");
+ synchronized (mLock) {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#applyNewResourceDirsLocked");
- String baseCodePath = appInfo.getBaseCodePath();
+ String baseCodePath = appInfo.getBaseCodePath();
- final int myUid = Process.myUid();
- String[] newSplitDirs = appInfo.uid == myUid
- ? appInfo.splitSourceDirs
- : appInfo.splitPublicSourceDirs;
+ final int myUid = Process.myUid();
+ String[] newSplitDirs = appInfo.uid == myUid
+ ? appInfo.splitSourceDirs
+ : appInfo.splitPublicSourceDirs;
- // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
- String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
- String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
- appInfo.overlayPaths);
+ // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
+ String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
+ String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
+ appInfo.overlayPaths);
- final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
- final int implCount = mResourceImpls.size();
- for (int i = 0; i < implCount; i++) {
- final ResourcesKey key = mResourceImpls.keyAt(i);
- final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
- final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+ final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+ final int implCount = mResourceImpls.size();
+ for (int i = 0; i < implCount; i++) {
+ final ResourcesKey key = mResourceImpls.keyAt(i);
+ final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
- if (impl == null) {
- continue;
+ if (impl == null) {
+ continue;
+ }
+
+ if (key.mResDir == null
+ || key.mResDir.equals(baseCodePath)
+ || ArrayUtils.contains(oldPaths, key.mResDir)) {
+ updatedResourceKeys.put(impl, new ResourcesKey(
+ baseCodePath,
+ copiedSplitDirs,
+ copiedResourceDirs,
+ key.mLibDirs,
+ key.mDisplayId,
+ key.mOverrideConfiguration,
+ key.mCompatInfo,
+ key.mLoaders
+ ));
+ }
}
- if (key.mResDir == null
- || key.mResDir.equals(baseCodePath)
- || ArrayUtils.contains(oldPaths, key.mResDir)) {
- updatedResourceKeys.put(impl, new ResourcesKey(
- baseCodePath,
- copiedSplitDirs,
- copiedResourceDirs,
- key.mLibDirs,
- key.mDisplayId,
- key.mOverrideConfiguration,
- key.mCompatInfo,
- key.mLoaders
- ));
- }
+ redirectResourcesToNewImplLocked(updatedResourceKeys);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
-
- redirectResourcesToNewImplLocked(updatedResourceKeys);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
@@ -1556,7 +1562,7 @@
public boolean overrideTokenDisplayAdjustments(IBinder token,
@Nullable Consumer<DisplayAdjustments> override) {
boolean handled = false;
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources tokenResources = mActivityResourceReferences.get(token);
if (tokenResources == null) {
return false;
@@ -1589,7 +1595,7 @@
@Override
public void onLoadersChanged(@NonNull Resources resources,
@NonNull List<ResourcesLoader> newLoader) {
- synchronized (ResourcesManager.this) {
+ synchronized (mLock) {
final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
if (oldKey == null) {
throw new IllegalArgumentException("Cannot modify resource loaders of"
@@ -1617,7 +1623,7 @@
**/
@Override
public void onLoaderUpdated(@NonNull ResourcesLoader loader) {
- synchronized (ResourcesManager.this) {
+ synchronized (mLock) {
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceImplKeys =
new ArrayMap<>();
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index c5c4277..dfef47d 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -586,6 +586,10 @@
* @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
*/
public Builder setDeviceAddress(String deviceAddress) {
+ if (deviceAddress == null) {
+ mDeviceAddress = deviceAddress;
+ return this;
+ }
return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7c7cfdb..bbb49fb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2022,12 +2022,9 @@
* <p>
* Output: Nothing.
* </p>
- *
- * @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE)
- @SystemApi
public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD =
"android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
@@ -2188,12 +2185,7 @@
* <p>
* Type: String
* </p>
- *
- * E.g. {@link android.Manifest.permission_group.CONTACTS}
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_PERMISSION_GROUP_NAME =
"android.intent.extra.PERMISSION_GROUP_NAME";
@@ -5342,28 +5334,19 @@
* {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
*
* E.g. an attribution tag could be location_provider, com.google.android.gms.*, etc.
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
/**
* A long representing the start timestamp (epoch time in millis) of the permission usage
* when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
/**
* A long representing the end timestamp (epoch time in millis) of the permission usage when
* used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
/**
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 9d381ef..804a06b 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -81,7 +81,4 @@
AndroidFuture<ParceledListSlice> getShortcuts(String packageName, int matchFlags, int userId);
AndroidFuture pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId);
-
- AndroidFuture updateShortcutVisibility(String callingPkg, String packageName,
- in byte[] certificate, in boolean visible, int userId);
}
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index a60e642..13ff602 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -20,6 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,6 +30,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* This class provides information for a shared library. There are
@@ -190,7 +192,8 @@
*
* @hide
*/
- public List<String> getAllCodePaths() {
+ @TestApi
+ public @NonNull List<String> getAllCodePaths() {
if (getPath() != null) {
// Builtin library.
ArrayList<String> list = new ArrayList<>();
@@ -198,7 +201,7 @@
return list;
} else {
// Static or dynamic library.
- return mCodePaths;
+ return Objects.requireNonNull(mCodePaths);
}
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 2a36c11..d77fa91 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -40,7 +40,6 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
@@ -790,23 +789,6 @@
}
}
- /**
- * Granting another app the access to the shortcuts you own. You must provide the package name
- * and their SHA256 certificate digest in order to granting the access.
- *
- * Once granted, the other app can retain a copy of all the shortcuts you own when calling
- * {@link LauncherApps#getShortcuts(LauncherApps.ShortcutQuery, UserHandle)}.
- */
- public void updateShortcutVisibility(@NonNull final String packageName,
- @Nullable final byte[] certificate, final boolean visible) {
- try {
- getFutureOrThrow(mService.updateShortcutVisibility(mContext.getPackageName(),
- packageName, certificate, visible, injectMyUserId()));
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
private static <T> T getFutureOrThrow(@NonNull AndroidFuture<T> future) {
try {
return future.get();
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 8bc3734..aa26520 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -39,6 +39,9 @@
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
}
]
+ },
+ {
+ "name": "CtsPackageManagerBootTestCases"
}
],
"postsubmit": [
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index 75fa1c1..a9d70c5 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -397,7 +397,7 @@
}
// CompatibilityMode is global state.
- if (!ParsingPackageUtils.sCompatibilityModeEnabled) {
+ if (!android.content.pm.PackageParser.sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 80b5078..8e3de61 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -21,9 +21,11 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.ImageFormat;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
+import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.SizeList;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
@@ -220,6 +222,7 @@
private InitializerFuture mInitFuture = null;
private ServiceConnection mConnection = null;
private ICameraExtensionsProxyService mProxy = null;
+ private boolean mSupportsAdvancedExtensions = false;
// Singleton, don't allow construction
private CameraExtensionManagerGlobal() {}
@@ -245,6 +248,11 @@
public void onServiceConnected(ComponentName component, IBinder binder) {
mProxy = ICameraExtensionsProxyService.Stub.asInterface(binder);
mInitFuture.setStatus(true);
+ try {
+ mSupportsAdvancedExtensions = mProxy.advancedExtensionsSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote IPC failed!");
+ }
}
};
ctx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE |
@@ -334,6 +342,10 @@
}
}
+ public boolean areAdvancedExtensionsSupported() {
+ return mSupportsAdvancedExtensions;
+ }
+
public IPreviewExtenderImpl initializePreviewExtension(int extensionType)
throws RemoteException {
synchronized (mLock) {
@@ -355,6 +367,17 @@
}
}
}
+
+ public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType)
+ throws RemoteException {
+ synchronized (mLock) {
+ if (mProxy != null) {
+ return mProxy.initializeAdvancedExtension(extensionType);
+ } else {
+ return null;
+ }
+ }
+ }
}
/**
@@ -374,23 +397,60 @@
/**
* @hide
*/
+ public static boolean areAdvancedExtensionsSupported() {
+ return CameraExtensionManagerGlobal.get().areAdvancedExtensionsSupported();
+ }
+
+ /**
+ * @hide
+ */
public static boolean isExtensionSupported(String cameraId, int extensionType,
CameraCharacteristics chars) {
- Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders;
+ if (areAdvancedExtensionsSupported()) {
+ try {
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extensionType);
+ return extender.isExtensionAvailable(cameraId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query extension availability! Extension service does not"
+ + " respond!");
+ return false;
+ }
+ } else {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders;
+ try {
+ extenders = initializeExtension(extensionType);
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+
+ try {
+ return extenders.first.isExtensionAvailable(cameraId, chars.getNativeMetadata()) &&
+ extenders.second.isExtensionAvailable(cameraId, chars.getNativeMetadata());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query extension availability! Extension service does not"
+ + " respond!");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static IAdvancedExtenderImpl initializeAdvancedExtension(@Extension int extensionType) {
+ IAdvancedExtenderImpl extender;
try {
- extenders = initializeExtension(extensionType);
- } catch (IllegalArgumentException e) {
- return false;
+ extender = CameraExtensionManagerGlobal.get().initializeAdvancedExtension(
+ extensionType);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to initialize extension: " + extensionType);
}
- try {
- return extenders.first.isExtensionAvailable(cameraId, chars.getNativeMetadata()) &&
- extenders.second.isExtensionAvailable(cameraId, chars.getNativeMetadata());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to query extension availability! Extension service does not"
- + " respond!");
- return false;
+ if (extender == null) {
+ throw new IllegalArgumentException("Unknown extension: " + extensionType);
}
+
+ return extender;
}
/**
@@ -487,13 +547,21 @@
throw new IllegalArgumentException("Unsupported extension");
}
- Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
- initializeExtension(extension);
StreamConfigurationMap streamMap = mChars.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- extenders.first.init(mCameraId, mChars.getNativeMetadata());
- return generateSupportedSizes(extenders.first.getSupportedResolutions(),
- ImageFormat.PRIVATE, streamMap);
+ if (areAdvancedExtensionsSupported()) {
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+ extender.init(mCameraId);
+ return generateSupportedSizes(
+ extender.getSupportedPreviewOutputResolutions(mCameraId),
+ ImageFormat.PRIVATE, streamMap);
+ } else {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.first.init(mCameraId, mChars.getNativeMetadata());
+ return generateSupportedSizes(extenders.first.getSupportedResolutions(),
+ ImageFormat.PRIVATE, streamMap);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
+ " not respond!");
@@ -536,31 +604,47 @@
throw new IllegalArgumentException("Unsupported extension");
}
- Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
- initializeExtension(extension);
StreamConfigurationMap streamMap = mChars.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- if (format == ImageFormat.YUV_420_888) {
- extenders.second.init(mCameraId, mChars.getNativeMetadata());
- if (extenders.second.getCaptureProcessor() == null) {
- // Extensions that don't implement any capture processor are limited to
- // JPEG only!
- return new ArrayList<>();
+ if (areAdvancedExtensionsSupported()) {
+ switch(format) {
+ case ImageFormat.YUV_420_888:
+ case ImageFormat.JPEG:
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported format: " + format);
}
- return generateSupportedSizes(extenders.second.getSupportedResolutions(),
- format, streamMap);
- } else if (format == ImageFormat.JPEG) {
- extenders.second.init(mCameraId, mChars.getNativeMetadata());
- if (extenders.second.getCaptureProcessor() != null) {
- // The framework will perform the additional encoding pass on the
- // processed YUV_420 buffers.
- return generateJpegSupportedSizes(
- extenders.second.getSupportedResolutions(), streamMap);
- } else {
- return generateSupportedSizes(null, format, streamMap);
- }
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+ extender.init(mCameraId);
+ return generateSupportedSizes(extender.getSupportedCaptureOutputResolutions(
+ mCameraId), format, streamMap);
} else {
- throw new IllegalArgumentException("Unsupported format: " + format);
+ if (format == ImageFormat.YUV_420_888) {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ if (extenders.second.getCaptureProcessor() == null) {
+ // Extensions that don't implement any capture processor are limited to
+ // JPEG only!
+ return new ArrayList<>();
+ }
+ return generateSupportedSizes(extenders.second.getSupportedResolutions(),
+ format, streamMap);
+ } else if (format == ImageFormat.JPEG) {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ if (extenders.second.getCaptureProcessor() != null) {
+ // The framework will perform the additional encoding pass on the
+ // processed YUV_420 buffers.
+ return generateJpegSupportedSizes(
+ extenders.second.getSupportedResolutions(), streamMap);
+ } else {
+ return generateSupportedSizes(null, format, streamMap);
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported format: " + format);
+ }
}
} finally {
unregisterClient(clientId);
@@ -608,6 +692,23 @@
if (!isExtensionSupported(mCameraId, extension, mChars)) {
throw new IllegalArgumentException("Unsupported extension");
}
+
+ if (areAdvancedExtensionsSupported()) {
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+ extender.init(mCameraId);
+ android.hardware.camera2.extension.Size sz =
+ new android.hardware.camera2.extension.Size();
+ sz.width = captureOutputSize.getWidth();
+ sz.height = captureOutputSize.getHeight();
+ LatencyRange latencyRange = extender.getEstimatedCaptureLatencyRange(mCameraId,
+ sz, format);
+ if (latencyRange != null) {
+ return new Range(latencyRange.min, latencyRange.max);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
+ + " not respond!");
} finally {
unregisterClient(clientId);
}
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 6ff68c1..d32341f 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -230,6 +230,33 @@
}
/**
+ * Takes ownership of the passed-in properties object
+ *
+ * <p>For internal use only</p>
+ * @hide
+ */
+ public CaptureResult(String cameraId, CameraMetadataNative results, CaptureRequest parent,
+ int requestId, long frameNumber) {
+ if (results == null) {
+ throw new IllegalArgumentException("results was null");
+ }
+
+ if (parent == null) {
+ throw new IllegalArgumentException("parent was null");
+ }
+
+ mResults = CameraMetadataNative.move(results);
+ if (mResults.isEmpty()) {
+ throw new AssertionError("Results must not be empty");
+ }
+ setNativeInstance(mResults);
+ mCameraId = cameraId;
+ mRequest = parent;
+ mSequenceId = requestId;
+ mFrameNumber = frameNumber;
+ }
+
+ /**
* Returns a copy of the underlying {@link CameraMetadataNative}.
* @hide
*/
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index df8eecc..ac7f2ca 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -94,6 +94,36 @@
}
/**
+ * Takes ownership of the passed-in camera metadata and the partial results
+ *
+ * @param partials a list of partial results; {@code null} will be substituted for an empty list
+ * @hide
+ */
+ public TotalCaptureResult(String logicalCameraId, CameraMetadataNative results,
+ CaptureRequest parent, int requestId, long frameNumber, List<CaptureResult> partials,
+ int sessionId, PhysicalCaptureResultInfo[] physicalResults) {
+ super(logicalCameraId, results, parent, requestId, frameNumber);
+
+ if (partials == null) {
+ mPartialResults = new ArrayList<>();
+ } else {
+ mPartialResults = partials;
+ }
+
+ mSessionId = sessionId;
+
+ mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
+ for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
+ TotalCaptureResult physicalResult = new TotalCaptureResult(
+ onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(),
+ parent, requestId, frameNumber, /*partials*/null, sessionId,
+ new PhysicalCaptureResultInfo[0]);
+ mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
+ physicalResult);
+ }
+ }
+
+ /**
* Creates a request-less result.
*
* <p><strong>For testing only.</strong></p>
diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
new file mode 100644
index 0000000..a61bb33
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.Size;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.view.Surface;
+
+/** @hide */
+parcelable CameraOutputConfig
+{
+ Size size;
+ Surface surface;
+ int imageFormat;
+ int capacity;
+
+ const int TYPE_SURFACE = 0;
+ const int TYPE_IMAGEREADER = 1;
+ const int TYPE_MULTIRES_IMAGEREADER = 2;
+ int type;
+
+ OutputConfigId outputId;
+ int surfaceGroupId;
+ String physicalCameraId;
+ List<OutputConfigId> surfaceSharingOutputConfigs;
+}
diff --git a/core/java/android/hardware/camera2/extension/CameraSessionConfig.aidl b/core/java/android/hardware/camera2/extension/CameraSessionConfig.aidl
new file mode 100644
index 0000000..97ce183
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/CameraSessionConfig.aidl
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.CameraOutputConfig;
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/** @hide */
+parcelable CameraSessionConfig
+{
+ List<CameraOutputConfig> outputConfigs;
+ CameraMetadataNative sessionParameter;
+ int sessionTemplateId;
+}
diff --git a/core/java/android/hardware/camera2/extension/CaptureFailure.aidl b/core/java/android/hardware/camera2/extension/CaptureFailure.aidl
new file mode 100644
index 0000000..d48696c
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/CaptureFailure.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.CaptureRequest;
+
+/** @hide */
+parcelable CaptureFailure
+{
+ CaptureRequest request;
+ int reason;
+ boolean dropped;
+ int sequenceId;
+ long frameNumber;
+ String errorPhysicalCameraId;
+}
diff --git a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
new file mode 100644
index 0000000..f279c59
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.ISessionProcessorImpl;
+import android.hardware.camera2.extension.LatencyRange;
+import android.hardware.camera2.extension.Size;
+import android.hardware.camera2.extension.SizeList;
+
+/** @hide */
+interface IAdvancedExtenderImpl
+{
+ boolean isExtensionAvailable(in String cameraId);
+ void init(in String cameraId);
+ LatencyRange getEstimatedCaptureLatencyRange(in String cameraId, in Size outputSize,
+ int format);
+ @nullable List<SizeList> getSupportedPreviewOutputResolutions(in String cameraId);
+ @nullable List<SizeList> getSupportedCaptureOutputResolutions(in String cameraId);
+ ISessionProcessorImpl getSessionProcessor();
+}
diff --git a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
index 2a6d22c..bc29e9a 100644
--- a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
+++ b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
@@ -15,6 +15,7 @@
*/
package android.hardware.camera2.extension;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
@@ -23,6 +24,8 @@
{
long registerClient();
void unregisterClient(long clientId);
+ boolean advancedExtensionsSupported();
@nullable IPreviewExtenderImpl initializePreviewExtension(int extensionType);
@nullable IImageCaptureExtenderImpl initializeImageExtension(int extensionType);
+ @nullable IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType);
}
diff --git a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
new file mode 100644
index 0000000..6ab0ad2
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.Request;
+
+/** @hide */
+interface ICaptureCallback
+{
+ void onCaptureStarted(int captureSequenceId, long timestamp);
+ void onCaptureProcessStarted(int captureSequenceId);
+ void onCaptureFailed(int captureSequenceId);
+ void onCaptureSequenceCompleted(int captureSequenceId);
+ void onCaptureSequenceAborted(int captureSequenceId);
+}
diff --git a/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl
new file mode 100644
index 0000000..f365469
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.ParcelImage;
+
+/** @hide */
+interface IImageProcessorImpl
+{
+ void onNextImageAvailable(in OutputConfigId outputConfigId, in ParcelImage image);
+}
diff --git a/core/java/android/hardware/camera2/extension/IRequestCallback.aidl b/core/java/android/hardware/camera2/extension/IRequestCallback.aidl
new file mode 100644
index 0000000..5f308b7
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IRequestCallback.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.CaptureFailure;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.extension.ParcelTotalCaptureResult;
+
+/** @hide */
+interface IRequestCallback
+{
+ void onCaptureStarted(int requestId, long frameNumber, long timestamp);
+ void onCaptureProgressed(int requestId, in ParcelCaptureResult partialResult);
+ void onCaptureCompleted(int requestId, in ParcelTotalCaptureResult totalCaptureResult);
+ void onCaptureFailed(int requestId, in CaptureFailure captureFailure);
+ void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId);
+ void onCaptureSequenceCompleted(int sequenceId, long frameNumber);
+ void onCaptureSequenceAborted(int sequenceId);
+}
diff --git a/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl
new file mode 100644
index 0000000..52595a8
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.IRequestCallback;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.Request;
+
+/** @hide */
+interface IRequestProcessorImpl
+{
+ void setImageProcessor(in OutputConfigId outputConfigId, in IImageProcessorImpl imageProcessor);
+ boolean submit(in Request request, in IRequestCallback callback);
+ boolean submitBurst(in List<Request> requests, in IRequestCallback callback);
+ boolean setRepeating(in Request request, in IRequestCallback callback);
+ void abortCaptures();
+ void stopRepeating();
+}
diff --git a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
new file mode 100644
index 0000000..6fdf4df
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.CameraSessionConfig;
+import android.hardware.camera2.extension.ICaptureCallback;
+import android.hardware.camera2.extension.IRequestProcessorImpl;
+import android.hardware.camera2.extension.OutputSurface;
+
+/** @hide */
+interface ISessionProcessorImpl
+{
+ CameraSessionConfig initSession(in String cameraId, in OutputSurface previewSurface,
+ in OutputSurface imageCaptureSurface);
+ void deInitSession();
+ void onCaptureSessionStart(IRequestProcessorImpl requestProcessor);
+ void onCaptureSessionEnd();
+ int startRepeating(in ICaptureCallback callback);
+ void stopRepeating();
+ int startCapture(in ICaptureCallback callback, int jpegRotation, int jpegQuality);
+}
diff --git a/core/java/android/hardware/camera2/extension/LatencyRange.aidl b/core/java/android/hardware/camera2/extension/LatencyRange.aidl
new file mode 100644
index 0000000..9bedbb0
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/LatencyRange.aidl
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+/** @hide */
+parcelable LatencyRange
+{
+ long min;
+ long max;
+}
diff --git a/core/java/android/hardware/camera2/extension/OutputConfigId.aidl b/core/java/android/hardware/camera2/extension/OutputConfigId.aidl
new file mode 100644
index 0000000..b27f29a
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/OutputConfigId.aidl
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+/** @hide */
+parcelable OutputConfigId
+{
+ int id;
+}
diff --git a/core/java/android/hardware/camera2/extension/OutputSurface.aidl b/core/java/android/hardware/camera2/extension/OutputSurface.aidl
new file mode 100644
index 0000000..8415379
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/OutputSurface.aidl
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.Size;
+import android.view.Surface;
+
+/** @hide */
+parcelable OutputSurface
+{
+ Surface surface;
+ Size size;
+ int imageFormat;
+}
diff --git a/core/java/android/hardware/camera2/extension/ParcelCaptureResult.aidl b/core/java/android/hardware/camera2/extension/ParcelCaptureResult.aidl
new file mode 100644
index 0000000..f99b256
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ParcelCaptureResult.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/** @hide */
+parcelable ParcelCaptureResult
+{
+ String cameraId;
+ CameraMetadataNative results;
+ CaptureRequest parent;
+ int sequenceId;
+ long frameNumber;
+}
diff --git a/core/java/android/hardware/camera2/extension/ParcelTotalCaptureResult.aidl b/core/java/android/hardware/camera2/extension/ParcelTotalCaptureResult.aidl
new file mode 100644
index 0000000..8021a57
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ParcelTotalCaptureResult.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
+
+/** @hide */
+parcelable ParcelTotalCaptureResult
+{
+ String logicalCameraId;
+ CameraMetadataNative results;
+ CaptureRequest parent;
+ int sequenceId;
+ long frameNumber;
+ List<ParcelCaptureResult> partials;
+ int sessionId;
+ List<PhysicalCaptureResultInfo> physicalResult;
+}
diff --git a/core/java/android/hardware/camera2/extension/Request.aidl b/core/java/android/hardware/camera2/extension/Request.aidl
new file mode 100644
index 0000000..d9934dc
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/Request.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/** @hide */
+parcelable Request
+{
+ List<OutputConfigId> targetOutputConfigIds;
+ CameraMetadataNative parameters;
+ int templateId;
+ int requestId;
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
new file mode 100644
index 0000000..abc487d
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -0,0 +1,917 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.impl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraExtensionCharacteristics;
+import android.hardware.camera2.CameraExtensionSession;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.extension.CameraOutputConfig;
+import android.hardware.camera2.extension.CameraSessionConfig;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
+import android.hardware.camera2.extension.ICaptureCallback;
+import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.IRequestCallback;
+import android.hardware.camera2.extension.IRequestProcessorImpl;
+import android.hardware.camera2.extension.ISessionProcessorImpl;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.OutputSurface;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.extension.ParcelImage;
+import android.hardware.camera2.extension.ParcelTotalCaptureResult;
+import android.hardware.camera2.extension.Request;
+import android.hardware.camera2.params.ExtensionSessionConfiguration;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSession {
+ private static final String TAG = "CameraAdvancedExtensionSessionImpl";
+
+ private final Executor mExecutor;
+ private final CameraDevice mCameraDevice;
+ private final long mExtensionClientId;
+ private final Handler mHandler;
+ private final HandlerThread mHandlerThread;
+ private final CameraExtensionSession.StateCallback mCallbacks;
+ private final IAdvancedExtenderImpl mAdvancedExtender;
+ // maps camera outputs to extension output ids
+ private final HashMap<Surface, Integer> mSurfaceIdMap = new HashMap<>();
+ // maps camera extension output ids to camera registered image readers
+ private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
+ private final RequestProcessor mRequestProcessor = new RequestProcessor();
+
+ private Surface mClientRepeatingRequestSurface;
+ private Surface mClientCaptureSurface;
+ private CameraCaptureSession mCaptureSession = null;
+ private ISessionProcessorImpl mSessionProcessor = null;
+
+ private boolean mInitialized;
+
+
+ // Lock to synchronize cross-thread access to device public interface
+ final Object mInterfaceLock = new Object(); // access from this class and Session only!
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CAMERA)
+ public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession(
+ @NonNull CameraDevice cameraDevice, @NonNull Context ctx,
+ @NonNull ExtensionSessionConfiguration config)
+ throws CameraAccessException, RemoteException {
+ long clientId = CameraExtensionCharacteristics.registerClient(ctx);
+ if (clientId < 0) {
+ throw new UnsupportedOperationException("Unsupported extension!");
+ }
+
+ String cameraId = cameraDevice.getId();
+ CameraManager manager = ctx.getSystemService(CameraManager.class);
+ CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
+ CameraExtensionCharacteristics extensionChars = new CameraExtensionCharacteristics(ctx,
+ cameraId, chars);
+
+ if (!CameraExtensionCharacteristics.isExtensionSupported(cameraDevice.getId(),
+ config.getExtension(), chars)) {
+ throw new UnsupportedOperationException("Unsupported extension type: " +
+ config.getExtension());
+ }
+
+ if (config.getOutputConfigurations().isEmpty() ||
+ config.getOutputConfigurations().size() > 2) {
+ throw new IllegalArgumentException("Unexpected amount of output surfaces, received: " +
+ config.getOutputConfigurations().size() + " expected <= 2");
+ }
+
+ int suitableSurfaceCount = 0;
+ List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
+ config.getExtension(), SurfaceTexture.class);
+ Surface repeatingRequestSurface = CameraExtensionUtils.getRepeatingRequestSurface(
+ config.getOutputConfigurations(), supportedPreviewSizes);
+ if (repeatingRequestSurface != null) {
+ suitableSurfaceCount++;
+ }
+
+ HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>();
+ for (int format : CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+ List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes(
+ config.getExtension(), format);
+ if (supportedSizes != null) {
+ supportedCaptureSizes.put(format, supportedSizes);
+ }
+ }
+ Surface burstCaptureSurface = CameraExtensionUtils.getBurstCaptureSurface(
+ config.getOutputConfigurations(), supportedCaptureSizes);
+ if (burstCaptureSurface != null) {
+ suitableSurfaceCount++;
+ }
+
+ if (suitableSurfaceCount != config.getOutputConfigurations().size()) {
+ throw new IllegalArgumentException("One or more unsupported output surfaces found!");
+ }
+
+ IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension(
+ config.getExtension());
+ extender.init(cameraId);
+
+ CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(clientId,
+ extender, cameraDevice, repeatingRequestSurface, burstCaptureSurface,
+ config.getStateCallback(), config.getExecutor());
+ ret.initialize();
+
+ return ret;
+ }
+
+ private CameraAdvancedExtensionSessionImpl(long extensionClientId,
+ @NonNull IAdvancedExtenderImpl extender, @NonNull CameraDevice cameraDevice,
+ @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
+ @NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor) {
+ mExtensionClientId = extensionClientId;
+ mAdvancedExtender = extender;
+ mCameraDevice = cameraDevice;
+ mCallbacks = callback;
+ mExecutor = executor;
+ mClientRepeatingRequestSurface = repeatingRequestSurface;
+ mClientCaptureSurface = burstCaptureSurface;
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mInitialized = false;
+ }
+
+ /**
+ * @hide
+ */
+ public synchronized void initialize() throws CameraAccessException, RemoteException {
+ if (mInitialized) {
+ Log.d(TAG, "Session already initialized");
+ return;
+ }
+
+ OutputSurface previewSurface = initializeParcelable(mClientRepeatingRequestSurface);
+ OutputSurface captureSurface = initializeParcelable(mClientCaptureSurface);
+ mSessionProcessor = mAdvancedExtender.getSessionProcessor();
+ CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mCameraDevice.getId(),
+ previewSurface, captureSurface);
+ List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
+ // map camera output ids to output configurations
+ HashMap<Integer, OutputConfiguration> cameraOutputs = new HashMap<>();
+ for (CameraOutputConfig output : outputConfigs) {
+ OutputConfiguration cameraOutput = null;
+ switch(output.type) {
+ case CameraOutputConfig.TYPE_SURFACE:
+ if (output.surface == null) {
+ Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
+ ", skipping!");
+ continue;
+ }
+ cameraOutput = new OutputConfiguration(output.surfaceGroupId,
+ output.surface);
+ break;
+ case CameraOutputConfig.TYPE_IMAGEREADER:
+ if ((output.imageFormat == ImageFormat.UNKNOWN) || (output.size.width <= 0) ||
+ (output.size.height <= 0)) {
+ Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
+ ", skipping!");
+ continue;
+ }
+ ImageReader reader = ImageReader.newInstance(output.size.width,
+ output.size.height, output.imageFormat, output.capacity);
+ mReaderMap.put(output.outputId.id, reader);
+ cameraOutput = new OutputConfiguration(output.surfaceGroupId,
+ reader.getSurface());
+ break;
+ case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER:
+ // TBD
+ default:
+ throw new IllegalArgumentException("Unsupported output config type: " +
+ output.type);
+ }
+ cameraOutput.setPhysicalCameraId(output.physicalCameraId);
+ cameraOutputs.put(output.outputId.id, cameraOutput);
+ }
+
+ ArrayList<OutputConfiguration> outputList = new ArrayList<>();
+ for (CameraOutputConfig output : outputConfigs) {
+ if (!cameraOutputs.containsKey(output.outputId.id)) {
+ // Shared surface already removed by a previous iteration
+ continue;
+ }
+ OutputConfiguration outConfig = cameraOutputs.get(output.outputId.id);
+ if ((output.surfaceSharingOutputConfigs != null) &&
+ !output.surfaceSharingOutputConfigs.isEmpty()) {
+ outConfig.enableSurfaceSharing();
+ for (OutputConfigId outputId : output.surfaceSharingOutputConfigs) {
+ outConfig.addSurface(cameraOutputs.get(outputId.id).getSurface());
+ cameraOutputs.remove(outputId.id);
+ }
+ }
+ outputList.add(outConfig);
+ mSurfaceIdMap.put(outConfig.getSurface(), output.outputId.id);
+ }
+
+ SessionConfiguration sessionConfiguration = new SessionConfiguration(
+ SessionConfiguration.SESSION_REGULAR, outputList,
+ new CameraExtensionUtils.HandlerExecutor(mHandler), new SessionStateHandler());
+
+ if ((sessionConfig.sessionParameter != null) &&
+ (!sessionConfig.sessionParameter.isEmpty())) {
+ CaptureRequest.Builder requestBuilder = mCameraDevice.createCaptureRequest(
+ sessionConfig.sessionTemplateId);
+ CaptureRequest sessionRequest = requestBuilder.build();
+ CameraMetadataNative.update(sessionRequest.getNativeMetadata(),
+ sessionConfig.sessionParameter);
+ sessionConfiguration.setSessionParameters(sessionRequest);
+ }
+
+ mCameraDevice.createCaptureSession(sessionConfiguration);
+ }
+
+ private static ParcelCaptureResult initializeParcelable(CaptureResult result) {
+ ParcelCaptureResult ret = new ParcelCaptureResult();
+ ret.cameraId = result.getCameraId();
+ ret.results = result.getNativeMetadata();
+ ret.parent = result.getRequest();
+ ret.sequenceId = result.getSequenceId();
+ ret.frameNumber = result.getFrameNumber();
+
+ return ret;
+ }
+
+ private static ParcelTotalCaptureResult initializeParcelable(TotalCaptureResult totalResult) {
+ ParcelTotalCaptureResult ret = new ParcelTotalCaptureResult();
+ ret.logicalCameraId = totalResult.getCameraId();
+ ret.results = totalResult.getNativeMetadata();
+ ret.parent = totalResult.getRequest();
+ ret.sequenceId = totalResult.getSequenceId();
+ ret.frameNumber = totalResult.getFrameNumber();
+ ret.sessionId = totalResult.getSessionId();
+ ret.partials = new ArrayList<>(totalResult.getPartialResults().size());
+ for (CaptureResult partial : totalResult.getPartialResults()) {
+ ret.partials.add(initializeParcelable(partial));
+ }
+ Map<String, TotalCaptureResult> physicalResults =
+ totalResult.getPhysicalCameraTotalResults();
+ ret.physicalResult = new ArrayList<>(physicalResults.size());
+ for (TotalCaptureResult physicalResult : physicalResults.values()) {
+ ret.physicalResult.add(new PhysicalCaptureResultInfo(physicalResult.getCameraId(),
+ physicalResult.getNativeMetadata()));
+ }
+
+ return ret;
+ }
+
+ private static OutputSurface initializeParcelable(Surface s) {
+ OutputSurface ret = new OutputSurface();
+ if (s != null) {
+ ret.surface = s;
+ ret.size = new android.hardware.camera2.extension.Size();
+ Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
+ ret.size.width = surfaceSize.getWidth();
+ ret.size.height = surfaceSize.getHeight();
+ ret.imageFormat = SurfaceUtils.getSurfaceFormat(s);
+ } else {
+ ret.surface = null;
+ ret.size = new android.hardware.camera2.extension.Size();
+ ret.size.width = -1;
+ ret.size.height = -1;
+ ret.imageFormat = ImageFormat.UNKNOWN;
+ }
+
+ return ret;
+ }
+
+ @Override
+ public @NonNull CameraDevice getDevice() {
+ synchronized (mInterfaceLock) {
+ return mCameraDevice;
+ }
+ }
+
+ @Override
+ public int setRepeatingRequest(@NonNull CaptureRequest request, @NonNull Executor executor,
+ @NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
+ int seqId = -1;
+ synchronized (mInterfaceLock) {
+ if (!mInitialized) {
+ throw new IllegalStateException("Uninitialized component");
+ }
+
+ if (mClientRepeatingRequestSurface == null) {
+ throw new IllegalArgumentException("No registered preview surface");
+ }
+
+ if (!request.containsTarget(mClientRepeatingRequestSurface) ||
+ (request.getTargets().size() != 1)) {
+ throw new IllegalArgumentException("Invalid repeating request output target!");
+ }
+
+ try {
+ seqId = mSessionProcessor.startRepeating(new RequestCallbackHandler(request,
+ executor, listener));
+ } catch (RemoteException e) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
+ "Failed to enable repeating request, extension service failed to respond!");
+ }
+ }
+
+ return seqId;
+ }
+
+ @Override
+ public int capture(@NonNull CaptureRequest request,
+ @NonNull Executor executor,
+ @NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
+ int seqId = -1;
+ synchronized (mInterfaceLock) {
+ if (!mInitialized) {
+ throw new IllegalStateException("Uninitialized component");
+ }
+
+ if (mClientCaptureSurface == null) {
+ throw new IllegalArgumentException("No output surface registered for single"
+ + " requests!");
+ }
+
+ if (!request.containsTarget(mClientCaptureSurface) ||
+ (request.getTargets().size() != 1)) {
+ throw new IllegalArgumentException("Invalid single capture output target!");
+ }
+
+ try {
+ // This will override the extension capture stage jpeg parameters with the user set
+ // jpeg quality and rotation. This will guarantee that client configured jpeg
+ // parameters always have highest priority.
+ Integer jpegRotation = request.get(CaptureRequest.JPEG_ORIENTATION);
+ if (jpegRotation == null) {
+ jpegRotation = CameraExtensionUtils.JPEG_DEFAULT_ROTATION;
+ }
+ Byte jpegQuality = request.get(CaptureRequest.JPEG_QUALITY);
+ if (jpegQuality == null) {
+ jpegQuality = CameraExtensionUtils.JPEG_DEFAULT_QUALITY;
+ }
+
+ seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
+ executor, listener), jpegRotation, jpegQuality);
+ } catch (RemoteException e) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
+ "Failed to submit capture request, extension service failed to respond!");
+ }
+ }
+
+ return seqId;
+ }
+
+ @Override
+ public void stopRepeating() throws CameraAccessException {
+ synchronized (mInterfaceLock) {
+ if (!mInitialized) {
+ throw new IllegalStateException("Uninitialized component");
+ }
+
+ mCaptureSession.stopRepeating();
+
+ try {
+ mSessionProcessor.stopRepeating();
+ } catch (RemoteException e) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
+ "Failed to notify about the end of repeating request, extension service"
+ + " failed to respond!");
+ }
+ }
+ }
+
+ @Override
+ public void close() throws CameraAccessException {
+ synchronized (mInterfaceLock) {
+ if (mInitialized) {
+ try {
+ mCaptureSession.stopRepeating();
+ mSessionProcessor.stopRepeating();
+ mSessionProcessor.onCaptureSessionEnd();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to stop the repeating request or end the session,"
+ + " , extension service does not respond!") ;
+ }
+ mCaptureSession.close();
+ }
+ }
+ }
+
+ public void release() {
+ synchronized (mInterfaceLock) {
+ mInitialized = false;
+ mHandlerThread.quitSafely();
+
+ if (mSessionProcessor != null) {
+ try {
+ mSessionProcessor.deInitSession();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to de-initialize session processor, extension service"
+ + " does not respond!") ;
+ }
+ mSessionProcessor = null;
+ }
+
+ if (mExtensionClientId >= 0) {
+ CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
+ }
+
+ for (ImageReader reader : mReaderMap.values()) {
+ reader.close();
+ }
+ mReaderMap.clear();
+
+ mClientRepeatingRequestSurface = null;
+ mClientCaptureSurface = null;
+ }
+ }
+
+ private void notifyConfigurationFailure() {
+ synchronized (mInterfaceLock) {
+ if (mInitialized) {
+ return;
+ }
+ }
+
+ release();
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallbacks.onConfigureFailed(
+ CameraAdvancedExtensionSessionImpl.this));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private class SessionStateHandler extends
+ android.hardware.camera2.CameraCaptureSession.StateCallback {
+ @Override
+ public void onClosed(@NonNull CameraCaptureSession session) {
+ release();
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallbacks.onClosed(
+ CameraAdvancedExtensionSessionImpl.this));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void onConfigureFailed(@NonNull CameraCaptureSession session) {
+ notifyConfigurationFailure();
+ }
+
+ @Override
+ public void onConfigured(@NonNull CameraCaptureSession session) {
+ boolean status = true;
+ synchronized (mInterfaceLock) {
+ mCaptureSession = session;
+ try {
+ mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
+ mInitialized = true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to start capture session,"
+ + " extension service does not respond!");
+ status = false;
+ session.close();
+ }
+ }
+
+ if (status) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallbacks.onConfigured(CameraAdvancedExtensionSessionImpl.this));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } else {
+ notifyConfigurationFailure();
+ }
+ }
+ }
+
+ private final class RequestCallbackHandler extends ICaptureCallback.Stub {
+ private final CaptureRequest mClientRequest;
+ private final Executor mClientExecutor;
+ private final ExtensionCaptureCallback mClientCallbacks;
+
+ private RequestCallbackHandler(@NonNull CaptureRequest clientRequest,
+ @NonNull Executor clientExecutor,
+ @NonNull ExtensionCaptureCallback clientCallbacks) {
+ mClientRequest = clientRequest;
+ mClientExecutor = clientExecutor;
+ mClientCallbacks = clientCallbacks;
+ }
+
+ @Override
+ public void onCaptureStarted(int captureSequenceId, long timestamp) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureStarted(
+ CameraAdvancedExtensionSessionImpl.this, mClientRequest,
+ timestamp));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void onCaptureProcessStarted(int captureSequenceId) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureProcessStarted(
+ CameraAdvancedExtensionSessionImpl.this, mClientRequest));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void onCaptureFailed(int captureSequenceId) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureFailed(
+ CameraAdvancedExtensionSessionImpl.this, mClientRequest));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int captureSequenceId) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureSequenceCompleted(
+ CameraAdvancedExtensionSessionImpl.this, captureSequenceId));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(int captureSequenceId) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureSequenceAborted(
+ CameraAdvancedExtensionSessionImpl.this, captureSequenceId));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ private final class CaptureCallbackHandler extends CameraCaptureSession.CaptureCallback {
+ private final IRequestCallback mCallback;
+
+ public CaptureCallbackHandler(IRequestCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onCaptureBufferLost(CameraCaptureSession session, CaptureRequest request,
+ Surface target, long frameNumber) {
+ try {
+ if (request.getTag() instanceof Integer) {
+ Integer requestId = (Integer) request.getTag();
+ mCallback.onCaptureBufferLost(requestId, frameNumber,
+ mSurfaceIdMap.get(target));
+ } else {
+ Log.e(TAG, "Invalid capture request tag!");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify lost capture buffer, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ try {
+ if (request.getTag() instanceof Integer) {
+ Integer requestId = (Integer) request.getTag();
+ mCallback.onCaptureCompleted(requestId, initializeParcelable(result));
+ } else {
+ Log.e(TAG, "Invalid capture request tag!");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture result, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
+ CaptureFailure failure) {
+ try {
+ if (request.getTag() instanceof Integer) {
+ Integer requestId = (Integer) request.getTag();
+ android.hardware.camera2.extension.CaptureFailure captureFailure =
+ new android.hardware.camera2.extension.CaptureFailure();
+ captureFailure.request = request;
+ captureFailure.reason = failure.getReason();
+ captureFailure.errorPhysicalCameraId = failure.getPhysicalCameraId();
+ captureFailure.frameNumber = failure.getFrameNumber();
+ captureFailure.sequenceId = failure.getSequenceId();
+ captureFailure.dropped = !failure.wasImageCaptured();
+ mCallback.onCaptureFailed(requestId, captureFailure);
+ } else {
+ Log.e(TAG, "Invalid capture request tag!");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture failure, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
+ CaptureResult partialResult) {
+ try {
+ if (request.getTag() instanceof Integer) {
+ Integer requestId = (Integer) request.getTag();
+ mCallback.onCaptureProgressed(requestId, initializeParcelable(partialResult));
+ } else {
+ Log.e(TAG, "Invalid capture request tag!");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture partial result, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(CameraCaptureSession session, int sequenceId) {
+ try {
+ mCallback.onCaptureSequenceAborted(sequenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify aborted sequence, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId,
+ long frameNumber) {
+ try {
+ mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify sequence complete, extension service doesn't"
+ + " respond!");
+ }
+ }
+
+ @Override
+ public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
+ long timestamp, long frameNumber) {
+ try {
+ if (request.getTag() instanceof Integer) {
+ Integer requestId = (Integer) request.getTag();
+ mCallback.onCaptureStarted(requestId, frameNumber, timestamp);
+ } else {
+ Log.e(TAG, "Invalid capture request tag!");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture started, extension service doesn't"
+ + " respond!");
+ }
+ }
+ }
+
+ private static final class ImageReaderHandler implements ImageReader.OnImageAvailableListener {
+ private final OutputConfigId mOutputConfigId;
+ private final IImageProcessorImpl mIImageProcessor;
+
+ private ImageReaderHandler(int outputConfigId,
+ IImageProcessorImpl iImageProcessor) {
+ mOutputConfigId = new OutputConfigId();
+ mOutputConfigId.id = outputConfigId;
+ mIImageProcessor = iImageProcessor;
+ }
+
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ if (mIImageProcessor == null) {
+ return;
+ }
+
+ Image img;
+ try {
+ img = reader.acquireNextImage();
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to acquire image, too many images pending!");
+ return;
+ }
+ if (img == null) {
+ Log.e(TAG, "Invalid image!");
+ return;
+ }
+
+ try {
+ reader.detachImage(img);
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to detach image");
+ img.close();
+ return;
+ }
+
+ ParcelImage parcelImage = new ParcelImage();
+ parcelImage.buffer = img.getHardwareBuffer();
+ if (img.getFenceFd() >= 0) {
+ try {
+ parcelImage.fence = ParcelFileDescriptor.fromFd(img.getFenceFd());
+ } catch (IOException e) {
+ Log.e(TAG,"Failed to parcel buffer fence!");
+ }
+ }
+ parcelImage.format = img.getFormat();
+ parcelImage.timestamp = img.getTimestamp();
+ parcelImage.transform = img.getTransform();
+ parcelImage.scalingMode = img.getScalingMode();
+ parcelImage.planeCount = img.getPlaneCount();
+ parcelImage.crop = img.getCropRect();
+
+ try {
+ mIImageProcessor.onNextImageAvailable(mOutputConfigId, parcelImage);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to propagate image buffer on output surface id: " +
+ mOutputConfigId + " extension service does not respond!");
+ } finally {
+ parcelImage.buffer.close();
+ img.close();
+ }
+ }
+ }
+
+ private final class RequestProcessor extends IRequestProcessorImpl.Stub {
+ @Override
+ public void setImageProcessor(OutputConfigId outputConfigId,
+ IImageProcessorImpl imageProcessor) {
+ synchronized (mInterfaceLock) {
+ if (mReaderMap.containsKey(outputConfigId.id)) {
+ mReaderMap.get(outputConfigId.id).setOnImageAvailableListener(
+ new ImageReaderHandler(outputConfigId.id, imageProcessor), mHandler);
+ } else {
+ Log.e(TAG, "ImageReader with output config id: " + outputConfigId.id +
+ " not found!");
+ }
+ }
+ }
+
+ @Override
+ public boolean submit(Request request, IRequestCallback callback) {
+ ArrayList<Request> captureList = new ArrayList<>();
+ captureList.add(request);
+ return submitBurst(captureList, callback);
+ }
+
+ @Override
+ public boolean submitBurst(List<Request> requests, IRequestCallback callback) {
+ synchronized (mInterfaceLock) {
+ try {
+ CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
+ ArrayList<CaptureRequest> captureRequests = new ArrayList<>();
+ for (Request request : requests) {
+ captureRequests.add(initializeCaptureRequest(mCameraDevice, request,
+ mSurfaceIdMap));
+ }
+ mCaptureSession.captureBurstRequests(captureRequests,
+ new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed to submit capture requests!");
+ return false;
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean setRepeating(Request request, IRequestCallback callback) {
+ synchronized (mInterfaceLock) {
+ try {
+ CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice,
+ request, mSurfaceIdMap);
+ CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
+ mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
+ new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed to enable repeating request!");
+ return false;
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void abortCaptures() {
+ synchronized (mInterfaceLock) {
+ try {
+ mCaptureSession.abortCaptures();
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed during capture abort!");
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
+ }
+ }
+ }
+
+ @Override
+ public void stopRepeating() {
+ synchronized (mInterfaceLock) {
+ try {
+ mCaptureSession.stopRepeating();
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed during repeating capture stop!");
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
+ }
+ }
+ }
+ }
+
+ private static CaptureRequest initializeCaptureRequest(CameraDevice cameraDevice,
+ Request request, HashMap<Surface, Integer> surfaceIdMap) throws CameraAccessException {
+ CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(request.templateId);
+ for (OutputConfigId configId : request.targetOutputConfigIds) {
+ boolean found = false;
+ for (Map.Entry<Surface, Integer> entry : surfaceIdMap.entrySet()) {
+ if (entry.getValue() == configId.id) {
+ builder.addTarget(entry.getKey());
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ Log.e(TAG, "Surface with output id: " + configId.id +
+ " not found among registered camera outputs!");
+ }
+ }
+
+ builder.setTag(request.requestId);
+ CaptureRequest ret = builder.build();
+ CameraMetadataNative.update(ret.getNativeMetadata(), request.parameters);
+ return ret;
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index b578bf8..11b137ca 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -25,6 +25,7 @@
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.CameraExtensionSession;
import android.hardware.camera2.CameraOfflineSession;
import android.hardware.camera2.CameraDevice;
@@ -138,6 +139,7 @@
private CameraCaptureSessionCore mCurrentSession;
private CameraExtensionSessionImpl mCurrentExtensionSession;
+ private CameraAdvancedExtensionSessionImpl mCurrentAdvancedExtensionSession;
private int mNextSessionId = 0;
private final int mAppTargetSdkVersion;
@@ -1343,6 +1345,12 @@
mCurrentExtensionSession.release();
mCurrentExtensionSession = null;
}
+
+ if (mCurrentAdvancedExtensionSession != null) {
+ mCurrentAdvancedExtensionSession.release();
+ mCurrentAdvancedExtensionSession = null;
+ }
+
// Only want to fire the onClosed callback once;
// either a normal close where the remote device is valid
// or a close after a startup error (no remote device but in error state)
@@ -2395,9 +2403,14 @@
public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
throws CameraAccessException {
try {
- mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(this,
- mContext,
- extensionConfiguration);
+ if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) {
+ mCurrentAdvancedExtensionSession =
+ CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession(
+ this, mContext, extensionConfiguration);
+ } else {
+ mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(
+ this, mContext, extensionConfiguration);
+ }
} catch (RemoteException e) {
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
}
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 936734b..3b1cb94 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -16,6 +16,9 @@
package android.hardware.camera2.impl;
+import static android.hardware.camera2.impl.CameraExtensionUtils.JPEG_DEFAULT_QUALITY;
+import static android.hardware.camera2.impl.CameraExtensionUtils.JPEG_DEFAULT_ROTATION;
+
import android.annotation.NonNull;
import android.graphics.ImageFormat;
import android.hardware.camera2.CaptureResult;
@@ -42,8 +45,6 @@
public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl {
public final static String TAG = "CameraExtensionJpeg";
private final static int JPEG_QUEUE_SIZE = 1;
- private final static int JPEG_DEFAULT_QUALITY = 100;
- private final static int JPEG_DEFAULT_ROTATION = 0;
private final Handler mHandler;
private final HandlerThread mHandlerThread;
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 3d771c01..5339f41 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -110,79 +110,10 @@
// Lock to synchronize cross-thread access to device public interface
final Object mInterfaceLock = new Object(); // access from this class and Session only!
- private static class SurfaceInfo {
- public int mWidth = 0;
- public int mHeight = 0;
- public int mFormat = PixelFormat.RGBA_8888;
- public long mUsage = 0;
- }
-
- private static final int SUPPORTED_CAPTURE_OUTPUT_FORMATS[] = {
- CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
- ImageFormat.JPEG
- };
-
- private static int nativeGetSurfaceWidth(Surface surface) {
- return SurfaceUtils.getSurfaceSize(surface).getWidth();
- }
-
- private static int nativeGetSurfaceHeight(Surface surface) {
- return SurfaceUtils.getSurfaceSize(surface).getHeight();
- }
-
private static int nativeGetSurfaceFormat(Surface surface) {
return SurfaceUtils.getSurfaceFormat(surface);
}
- private static Surface getBurstCaptureSurface(
- @NonNull List<OutputConfiguration> outputConfigs,
- @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
- for (OutputConfiguration config : outputConfigs) {
- SurfaceInfo surfaceInfo = querySurface(config.getSurface());
- for (int supportedFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
- if (surfaceInfo.mFormat == supportedFormat) {
- Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
- if (supportedCaptureSizes.containsKey(supportedFormat)) {
- if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
- return config.getSurface();
- } else {
- throw new IllegalArgumentException("Capture size not supported!");
- }
- }
- return config.getSurface();
- }
- }
- }
-
- return null;
- }
-
- private static @Nullable Surface getRepeatingRequestSurface(
- @NonNull List<OutputConfiguration> outputConfigs,
- @Nullable List<Size> supportedPreviewSizes) {
- for (OutputConfiguration config : outputConfigs) {
- SurfaceInfo surfaceInfo = querySurface(config.getSurface());
- if ((surfaceInfo.mFormat ==
- CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
- // The default RGBA_8888 is also implicitly supported because camera will
- // internally override it to
- // 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
- (surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
- Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
- surfaceInfo.mHeight);
- if ((supportedPreviewSizes == null) ||
- (!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
- throw new IllegalArgumentException("Repeating request surface size " +
- repeatingRequestSurfaceSize + " not supported!");
- }
-
- return config.getSurface();
- }
- }
-
- return null;
- }
-
/**
* @hide
*/
@@ -221,22 +152,22 @@
int suitableSurfaceCount = 0;
List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
config.getExtension(), SurfaceTexture.class);
- Surface repeatingRequestSurface = getRepeatingRequestSurface(
+ Surface repeatingRequestSurface = CameraExtensionUtils.getRepeatingRequestSurface(
config.getOutputConfigurations(), supportedPreviewSizes);
if (repeatingRequestSurface != null) {
suitableSurfaceCount++;
}
HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>();
- for (int format : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+ for (int format : CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes(
config.getExtension(), format);
if (supportedSizes != null) {
supportedCaptureSizes.put(format, supportedSizes);
}
}
- Surface burstCaptureSurface = getBurstCaptureSurface(config.getOutputConfigurations(),
- supportedCaptureSizes);
+ Surface burstCaptureSurface = CameraExtensionUtils.getBurstCaptureSurface(
+ config.getOutputConfigurations(), supportedCaptureSizes);
if (burstCaptureSurface != null) {
suitableSurfaceCount++;
}
@@ -266,15 +197,15 @@
return session;
}
- private CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
- @NonNull IPreviewExtenderImpl previewExtender,
- @NonNull List<Size> previewSizes,
- long extensionClientId,
- @NonNull CameraDevice cameraDevice,
- @Nullable Surface repeatingRequestSurface,
- @Nullable Surface burstCaptureSurface,
- @NonNull StateCallback callback,
- @NonNull Executor executor) {
+ public CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
+ @NonNull IPreviewExtenderImpl previewExtender,
+ @NonNull List<Size> previewSizes,
+ long extensionClientId,
+ @NonNull CameraDevice cameraDevice,
+ @Nullable Surface repeatingRequestSurface,
+ @Nullable Surface burstCaptureSurface,
+ @NonNull StateCallback callback,
+ @NonNull Executor executor) {
mExtensionClientId = extensionClientId;
mImageExtender = imageExtender;
mPreviewExtender = previewExtender;
@@ -290,57 +221,17 @@
mInitialized = false;
}
- private static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
- ImageWriter writer = null;
- Image img = null;
- SurfaceInfo surfaceInfo = new SurfaceInfo();
- int nativeFormat = nativeGetSurfaceFormat(s);
- int dataspace = SurfaceUtils.getSurfaceDataspace(s);
- // Jpeg surfaces cannot be queried for their usage and other parameters
- // in the usual way below. A buffer can only be de-queued after the
- // producer overrides the surface dimensions to (width*height) x 1.
- if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
- (dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
- surfaceInfo.mFormat = ImageFormat.JPEG;
- surfaceInfo.mWidth = nativeGetSurfaceWidth(s);
- surfaceInfo.mHeight = nativeGetSurfaceHeight(s);
- return surfaceInfo;
- }
-
- HardwareBuffer buffer = null;
- try {
- writer = ImageWriter.newInstance(s, 1);
- img = writer.dequeueInputImage();
- buffer = img.getHardwareBuffer();
- surfaceInfo.mFormat = buffer.getFormat();
- surfaceInfo.mWidth = buffer.getWidth();
- surfaceInfo.mHeight = buffer.getHeight();
- surfaceInfo.mUsage = buffer.getUsage();
- } catch (Exception e) {
- Log.e(TAG, "Failed to query surface, returning defaults!");
- } finally {
- if (buffer != null) {
- buffer.close();
- }
- if (img != null) {
- img.close();
- }
- if (writer != null) {
- writer.close();
- }
- }
-
- return surfaceInfo;
- }
-
private void initializeRepeatingRequestPipeline() throws RemoteException {
- SurfaceInfo repeatingSurfaceInfo = new SurfaceInfo();
+ CameraExtensionUtils.SurfaceInfo repeatingSurfaceInfo =
+ new CameraExtensionUtils.SurfaceInfo();
mPreviewProcessorType = mPreviewExtender.getProcessorType();
if (mClientRepeatingRequestSurface != null) {
- repeatingSurfaceInfo = querySurface(mClientRepeatingRequestSurface);
+ repeatingSurfaceInfo = CameraExtensionUtils.querySurface(
+ mClientRepeatingRequestSurface);
} else {
// Make the intermediate surface behave as any regular 'SurfaceTexture'
- SurfaceInfo captureSurfaceInfo = querySurface(mClientCaptureSurface);
+ CameraExtensionUtils.SurfaceInfo captureSurfaceInfo = CameraExtensionUtils.querySurface(
+ mClientCaptureSurface);
Size captureSize = new Size(captureSurfaceInfo.mWidth, captureSurfaceInfo.mHeight);
Size previewSize = findSmallestAspectMatchedSize(mSupportedPreviewSizes, captureSize);
repeatingSurfaceInfo.mWidth = previewSize.getWidth();
@@ -418,7 +309,8 @@
if (mImageProcessor != null) {
if (mClientCaptureSurface != null) {
- SurfaceInfo surfaceInfo = querySurface(mClientCaptureSurface);
+ CameraExtensionUtils.SurfaceInfo surfaceInfo = CameraExtensionUtils.querySurface(
+ mClientCaptureSurface);
if (surfaceInfo.mFormat == ImageFormat.JPEG) {
mImageJpegProcessor = new CameraExtensionJpegProcessor(mImageProcessor);
mImageProcessor = mImageJpegProcessor;
@@ -501,7 +393,7 @@
SessionConfiguration sessionConfig = new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR,
outputList,
- new HandlerExecutor(mHandler),
+ new CameraExtensionUtils.HandlerExecutor(mHandler),
new SessionStateHandler());
if (!sessionParamsList.isEmpty()) {
@@ -656,7 +548,8 @@
throw new UnsupportedOperationException("Failed to create still capture burst request");
}
- return mCaptureSession.captureBurstRequests(burstRequest, new HandlerExecutor(mHandler),
+ return mCaptureSession.captureBurstRequests(burstRequest,
+ new CameraExtensionUtils.HandlerExecutor(mHandler),
new BurstRequestHandler(request, executor, listener, requestMap,
mBurstCaptureImageCallback));
}
@@ -724,7 +617,7 @@
CaptureRequest repeatingRequest = createRequest(mCameraDevice,
captureStageList, mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW);
return mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
- new HandlerExecutor(mHandler), requestHandler);
+ new CameraExtensionUtils.HandlerExecutor(mHandler), requestHandler);
}
/** @hide */
@@ -1705,23 +1598,6 @@
return ret;
}
- private final class HandlerExecutor implements Executor {
- private final Handler mHandler;
-
- public HandlerExecutor(Handler handler) {
- mHandler = handler;
- }
-
- @Override
- public void execute(Runnable runCmd) {
- try {
- mHandler.post(runCmd);
- } catch (RejectedExecutionException e) {
- Log.w(TAG, "Handler thread unavailable, skipping message!");
- }
- }
- }
-
private static ParcelImage initializeParcelImage(Image img) {
ParcelImage parcelImage = new ParcelImage();
parcelImage.buffer = img.getHardwareBuffer();
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
new file mode 100644
index 0000000..950d716b
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.impl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
+import android.hardware.camera2.CameraExtensionCharacteristics;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.Image;
+import android.media.ImageWriter;
+import android.os.Handler;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+public final class CameraExtensionUtils {
+ private static final String TAG = "CameraExtensionUtils";
+
+ public final static int JPEG_DEFAULT_QUALITY = 100;
+ public final static int JPEG_DEFAULT_ROTATION = 0;
+
+ public static final int[] SUPPORTED_CAPTURE_OUTPUT_FORMATS = {
+ CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
+ ImageFormat.JPEG
+ };
+
+ public static class SurfaceInfo {
+ public int mWidth = 0;
+ public int mHeight = 0;
+ public int mFormat = PixelFormat.RGBA_8888;
+ public long mUsage = 0;
+ }
+
+ public static final class HandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public HandlerExecutor(Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void execute(Runnable runCmd) {
+ try {
+ mHandler.post(runCmd);
+ } catch (RejectedExecutionException e) {
+ Log.w(TAG, "Handler thread unavailable, skipping message!");
+ }
+ }
+ }
+
+ public static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
+ ImageWriter writer = null;
+ Image img = null;
+ SurfaceInfo surfaceInfo = new SurfaceInfo();
+ int nativeFormat = SurfaceUtils.getSurfaceFormat(s);
+ int dataspace = SurfaceUtils.getSurfaceDataspace(s);
+ // Jpeg surfaces cannot be queried for their usage and other parameters
+ // in the usual way below. A buffer can only be de-queued after the
+ // producer overrides the surface dimensions to (width*height) x 1.
+ if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
+ (dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
+ surfaceInfo.mFormat = ImageFormat.JPEG;
+ Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
+ surfaceInfo.mWidth = surfaceSize.getWidth();
+ surfaceInfo.mHeight = surfaceSize.getHeight();
+ return surfaceInfo;
+ }
+
+ HardwareBuffer buffer = null;
+ try {
+ writer = ImageWriter.newInstance(s, 1);
+ img = writer.dequeueInputImage();
+ buffer = img.getHardwareBuffer();
+ surfaceInfo.mFormat = buffer.getFormat();
+ surfaceInfo.mWidth = buffer.getWidth();
+ surfaceInfo.mHeight = buffer.getHeight();
+ surfaceInfo.mUsage = buffer.getUsage();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to query surface, returning defaults!");
+ } finally {
+ if (buffer != null) {
+ buffer.close();
+ }
+ if (img != null) {
+ img.close();
+ }
+ if (writer != null) {
+ writer.close();
+ }
+ }
+
+ return surfaceInfo;
+ }
+
+ public static Surface getBurstCaptureSurface(
+ @NonNull List<OutputConfiguration> outputConfigs,
+ @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
+ for (OutputConfiguration config : outputConfigs) {
+ SurfaceInfo surfaceInfo = querySurface(config.getSurface());
+ for (int supportedFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+ if (surfaceInfo.mFormat == supportedFormat) {
+ Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
+ if (supportedCaptureSizes.containsKey(supportedFormat)) {
+ if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
+ return config.getSurface();
+ } else {
+ throw new IllegalArgumentException("Capture size not supported!");
+ }
+ }
+ return config.getSurface();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public static @Nullable Surface getRepeatingRequestSurface(
+ @NonNull List<OutputConfiguration> outputConfigs,
+ @Nullable List<Size> supportedPreviewSizes) {
+ for (OutputConfiguration config : outputConfigs) {
+ SurfaceInfo surfaceInfo = querySurface(config.getSurface());
+ if ((surfaceInfo.mFormat ==
+ CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
+ // The default RGBA_8888 is also implicitly supported because camera will
+ // internally override it to
+ // 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
+ (surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
+ Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
+ surfaceInfo.mHeight);
+ if ((supportedPreviewSizes == null) ||
+ (!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
+ throw new IllegalArgumentException("Repeating request surface size " +
+ repeatingRequestSurfaceSize + " not supported!");
+ }
+
+ return config.getSurface();
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 9a27a99..55c90ce 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -69,8 +69,6 @@
private static final int MSG_SET_FEATURE_COMPLETED = 107;
private static final int MSG_CHALLENGE_GENERATED = 108;
private static final int MSG_FACE_DETECTED = 109;
- private static final int MSG_CHALLENGE_INTERRUPTED = 110;
- private static final int MSG_CHALLENGE_INTERRUPT_FINISHED = 111;
private static final int MSG_AUTHENTICATION_FRAME = 112;
private static final int MSG_ENROLLMENT_FRAME = 113;
@@ -102,8 +100,8 @@
@Override // binder call
public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) {
- mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0,
- face).sendToTarget();
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId,
+ isStrongBiometric ? 1 : 0, face).sendToTarget();
}
@Override // binder call
@@ -142,22 +140,12 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) {
- mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge)
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
+ mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, userId, challenge)
.sendToTarget();
}
@Override
- public void onChallengeInterrupted(int sensorId) {
- mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPTED, sensorId).sendToTarget();
- }
-
- @Override
- public void onChallengeInterruptFinished(int sensorId) {
- mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPT_FINISHED, sensorId).sendToTarget();
- }
-
- @Override
public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
mHandler.obtainMessage(MSG_AUTHENTICATION_FRAME, frame).sendToTarget();
}
@@ -434,16 +422,14 @@
*
* @see com.android.server.locksettings.LockSettingsService
*
- * TODO(b/171335732): should take userId
- *
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void generateChallenge(int sensorId, GenerateChallengeCallback callback) {
+ public void generateChallenge(int sensorId, int userId, GenerateChallengeCallback callback) {
if (mService != null) {
try {
mGenerateChallengeCallback = callback;
- mService.generateChallenge(mToken, sensorId, 0 /* userId */, mServiceReceiver,
+ mService.generateChallenge(mToken, sensorId, userId, mServiceReceiver,
mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -452,12 +438,13 @@
}
/**
- * Same as {@link #generateChallenge(int, GenerateChallengeCallback)}, but assumes the first
- * enumerated sensor.
+ * Same as {@link #generateChallenge(int, int, GenerateChallengeCallback)}, but assumes the
+ * first enumerated sensor.
+ *
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void generateChallenge(GenerateChallengeCallback callback) {
+ public void generateChallenge(int userId, GenerateChallengeCallback callback) {
final List<FaceSensorPropertiesInternal> faceSensorProperties =
getSensorPropertiesInternal();
if (faceSensorProperties.isEmpty()) {
@@ -466,7 +453,7 @@
}
final int sensorId = faceSensorProperties.get(0).sensorId;
- generateChallenge(sensorId, callback);
+ generateChallenge(sensorId, userId, callback);
}
/**
@@ -1120,25 +1107,16 @@
}
/**
- * Callback structure provided to {@link #generateChallenge(int, GenerateChallengeCallback)}.
+ * Callback structure provided to {@link #generateChallenge(int, int,
+ * GenerateChallengeCallback)}.
+ *
* @hide
*/
public interface GenerateChallengeCallback {
/**
* Invoked when a challenge has been generated.
*/
- void onGenerateChallengeResult(int sensorId, long challenge);
-
- /**
- * Invoked if the challenge has not been revoked and a subsequent caller/owner invokes
- * {@link #generateChallenge(int, GenerateChallengeCallback)}, but
- */
- default void onChallengeInterrupted(int sensorId) {}
-
- /**
- * Invoked when the interrupting client has finished (e.g. revoked its challenge).
- */
- default void onChallengeInterruptFinished(int sensorId) {}
+ void onGenerateChallengeResult(int sensorId, int userId, long challenge);
}
private class OnEnrollCancelListener implements OnCancelListener {
@@ -1212,18 +1190,13 @@
args.recycle();
break;
case MSG_CHALLENGE_GENERATED:
- sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */);
+ sendChallengeGenerated(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
+ (long) msg.obj /* challenge */);
break;
case MSG_FACE_DETECTED:
sendFaceDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
(boolean) msg.obj /* isStrongBiometric */);
break;
- case MSG_CHALLENGE_INTERRUPTED:
- sendChallengeInterrupted((int) msg.obj /* sensorId */);
- break;
- case MSG_CHALLENGE_INTERRUPT_FINISHED:
- sendChallengeInterruptFinished((int) msg.obj /* sensorId */);
- break;
case MSG_AUTHENTICATION_FRAME:
sendAuthenticationFrame((FaceAuthenticationFrame) msg.obj /* frame */);
break;
@@ -1251,11 +1224,11 @@
mGetFeatureCallback.onCompleted(success, features, featureState);
}
- private void sendChallengeGenerated(int sensorId, long challenge) {
+ private void sendChallengeGenerated(int sensorId, int userId, long challenge) {
if (mGenerateChallengeCallback == null) {
return;
}
- mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, challenge);
+ mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, userId, challenge);
}
private void sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
@@ -1266,22 +1239,6 @@
mFaceDetectionCallback.onFaceDetected(sensorId, userId, isStrongBiometric);
}
- private void sendChallengeInterrupted(int sensorId) {
- if (mGenerateChallengeCallback == null) {
- Slog.e(TAG, "sendChallengeInterrupted, callback null");
- return;
- }
- mGenerateChallengeCallback.onChallengeInterrupted(sensorId);
- }
-
- private void sendChallengeInterruptFinished(int sensorId) {
- if (mGenerateChallengeCallback == null) {
- Slog.e(TAG, "sendChallengeInterruptFinished, callback null");
- return;
- }
- mGenerateChallengeCallback.onChallengeInterruptFinished(sensorId);
- }
-
private void sendRemovedResult(Face face, int remaining) {
if (mRemovalCallback == null) {
return;
diff --git a/core/java/android/hardware/face/FaceServiceReceiver.java b/core/java/android/hardware/face/FaceServiceReceiver.java
index 9e62ca5..9e78592 100644
--- a/core/java/android/hardware/face/FaceServiceReceiver.java
+++ b/core/java/android/hardware/face/FaceServiceReceiver.java
@@ -72,17 +72,8 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
-
- }
-
- @Override
- public void onChallengeInterrupted(int sensorId) throws RemoteException {
-
- }
-
- @Override
- public void onChallengeInterruptFinished(int sensorId) throws RemoteException {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge)
+ throws RemoteException {
}
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index 0ccb395..c4d9bf2 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -33,9 +33,7 @@
void onRemoved(in Face face, int remaining);
void onFeatureSet(boolean success, int feature);
void onFeatureGet(boolean success, in int[] features, in boolean[] featureState);
- void onChallengeGenerated(int sensorId, long challenge);
- void onChallengeInterrupted(int sensorId);
- void onChallengeInterruptFinished(int sensorId);
+ void onChallengeGenerated(int sensorId, int userId, long challenge);
void onAuthenticationFrame(in FaceAuthenticationFrame frame);
void onEnrollmentFrame(in FaceEnrollFrame frame);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b52955d..8aeb5cd 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -475,10 +475,13 @@
}
/**
+ * Callbacks for generate challenge operations.
+ *
* @hide
*/
public interface GenerateChallengeCallback {
- void onChallengeGenerated(int sensorId, long challenge);
+ /** Called when a challenged has been generated. */
+ void onChallengeGenerated(int sensorId, int userId, long challenge);
}
/**
@@ -1124,7 +1127,8 @@
sendRemovedResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
break;
case MSG_CHALLENGE_GENERATED:
- sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */);
+ sendChallengeGenerated(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
+ (long) msg.obj /* challenge */);
break;
case MSG_FINGERPRINT_DETECTED:
sendFingerprintDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
@@ -1233,12 +1237,12 @@
}
}
- private void sendChallengeGenerated(int sensorId, long challenge) {
+ private void sendChallengeGenerated(int sensorId, int userId, long challenge) {
if (mGenerateChallengeCallback == null) {
Slog.e(TAG, "sendChallengeGenerated, callback null");
return;
}
- mGenerateChallengeCallback.onChallengeGenerated(sensorId, challenge);
+ mGenerateChallengeCallback.onChallengeGenerated(sensorId, userId, challenge);
}
private void sendFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) {
@@ -1454,8 +1458,8 @@
}
@Override // binder call
- public void onChallengeGenerated(int sensorId, long challenge) {
- mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge)
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
+ mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, userId, challenge)
.sendToTarget();
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java b/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
index 798e87b..a9779b5 100644
--- a/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
+++ b/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
@@ -61,7 +61,8 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge)
+ throws RemoteException {
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 1bd284d..9cea1fe 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -29,7 +29,7 @@
void onAuthenticationFailed();
void onError(int error, int vendorCode);
void onRemoved(in Fingerprint fp, int remaining);
- void onChallengeGenerated(int sensorId, long challenge);
+ void onChallengeGenerated(int sensorId, int userId, long challenge);
void onUdfpsPointerDown(int sensorId);
void onUdfpsPointerUp(int sensorId);
}
diff --git a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
index 1551e07..f4d22da 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
@@ -35,7 +35,7 @@
* UdfpsController will call this method when the HBM is enabled.
*
* @param hbmType The type of HBM that was enabled. See
- * {@link com.android.systemui.biometrics.HbmTypes}.
+ * {@link com.android.systemui.biometrics.UdfpsHbmTypes}.
* @param displayId The displayId for which the HBM is enabled. See
* {@link android.view.Display#getDisplayId()}.
*/
@@ -45,7 +45,7 @@
* UdfpsController will call this method when the HBM is disabled.
*
* @param hbmType The type of HBM that was disabled. See
- * {@link com.android.systemui.biometrics.HbmTypes}.
+ * {@link com.android.systemui.biometrics.UdfpsHbmTypes}.
* @param displayId The displayId for which the HBM is disabled. See
* {@link android.view.Display#getDisplayId()}.
*/
diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS
index 16c15e3..60d43fd 100644
--- a/core/java/android/hardware/hdmi/OWNERS
+++ b/core/java/android/hardware/hdmi/OWNERS
@@ -4,3 +4,4 @@
marvinramin@google.com
nchalko@google.com
+lcnathalie@google.com
diff --git a/core/java/android/net/NetworkStateSnapshot.java b/core/java/android/net/NetworkStateSnapshot.java
index 9df861a..3915634 100644
--- a/core/java/android/net/NetworkStateSnapshot.java
+++ b/core/java/android/net/NetworkStateSnapshot.java
@@ -104,7 +104,10 @@
return mSubscriberId;
}
- /** Get the legacy type of the network associated with this snapshot. */
+ /**
+ * Get the legacy type of the network associated with this snapshot.
+ * @return the legacy network type. See {@code ConnectivityManager#TYPE_*}.
+ */
public int getLegacyType() {
return mLegacyType;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c97d1f8..4c8297a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3931,7 +3931,6 @@
getStartClockTime(),
whichBatteryScreenOffRealtime / 1000, whichBatteryScreenOffUptime / 1000,
getEstimatedBatteryCapacity(),
- getLearnedBatteryCapacity(),
getMinLearnedBatteryCapacity(),
getMaxLearnedBatteryCapacity(),
screenDozeTime / 1000);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 38ed3fb..900659b 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -309,7 +309,7 @@
* booted, but it may increase when the hardware manufacturer provides an OTA update.
* <p>
* Possible non-zero values are defined in {@link Build.VERSION_CODES} starting with
- * {@link Build.VERSION_CODES#S}.
+ * {@link Build.VERSION_CODES#R}.
*/
public static final int MEDIA_PERFORMANCE_CLASS =
DeviceProperties.media_performance_class().orElse(0);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3bdc546..b879082 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9921,6 +9921,18 @@
"accessibility_floating_menu_opacity";
/**
+ * Prompts the user to the Accessibility button is replaced with the floating menu.
+ * <ul>
+ * <li> 0 = disabled </li>
+ * <li> 1 = enabled </li>
+ * </ul>
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT =
+ "accessibility_floating_menu_migration_tooltip_prompt";
+
+ /**
* Whether the Adaptive connectivity option is enabled.
*
* @hide
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 53bde36..324d1ab 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -21,6 +21,7 @@
import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import android.annotation.FloatRange;
import android.annotation.NonNull;
@@ -1218,7 +1219,9 @@
mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
mCaller.getHandler());
mDisplay = mIWallpaperEngine.mDisplay;
- mDisplayContext = createDisplayContext(mDisplay);
+ // Use window context of TYPE_WALLPAPER so client can access UI resources correctly.
+ mDisplayContext = createDisplayContext(mDisplay)
+ .createWindowContext(TYPE_WALLPAPER, null /* options */);
mDisplayState = mDisplay.getState();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/AttachedSurfaceControl.java
similarity index 70%
rename from core/java/android/view/ViewRoot.java
rename to core/java/android/view/AttachedSurfaceControl.java
index 3c75598..bcc5b56 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -22,16 +22,19 @@
/**
* Provides an interface to the root-Surface of a View Hierarchy or Window. This
* is used in combination with the {@link android.view.SurfaceControl} API to enable
- * attaching app created SurfaceControl to the ViewRoot's surface hierarchy, and enable
- * SurfaceTransactions to be performed in sync with the ViewRoot drawing. This object
- * is obtained from {@link android.view.View#getViewRoot} and
- * {@link android.view.Window#getViewRoot}. It must be used from the UI thread of
+ * attaching app created SurfaceControl to the SurfaceControl hierarchy used
+ * by the app, and enable SurfaceTransactions to be performed in sync with the
+ * View hierarchy drawing.
+ *
+ * This object is obtained from {@link android.view.View#getRootSurfaceControl} and
+ * {@link android.view.Window#getRootSurfaceControl}. It must be used from the UI thread of
* the object it was obtained from.
*/
@UiThread
-public interface ViewRoot {
+public interface AttachedSurfaceControl {
/**
- * Create a transaction which will reparent {@param child} to the ViewRoot. See
+ * Create a transaction which will reparent {@param child} to the View hierarchy
+ * root SurfaceControl. See
* {@link SurfaceControl.Transaction#reparent}. This transacton must be applied
* or merged in to another transaction by the caller, otherwise it will have
* no effect.
@@ -42,9 +45,9 @@
@Nullable SurfaceControl.Transaction buildReparentTransaction(@NonNull SurfaceControl child);
/**
- * Consume the passed in transaction, and request the ViewRoot to apply it with the
- * next draw. This transaction will be merged with the buffer transaction from the ViewRoot
- * and they will show up on-screen atomically synced.
+ * Consume the passed in transaction, and request the View hierarchy to apply it atomically
+ * with the next draw. This transaction will be merged with the buffer transaction from the
+ * ViewRoot and they will show up on-screen atomically synced.
*
* This will not cause a draw to be scheduled, and if there are no other changes
* to the View hierarchy you may need to call {@link android.view.View#invalidate}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c1c892c..acc0fc1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -29599,7 +29599,7 @@
return mScrollCaptureInternal;
}
- ViewRoot getViewRoot() {
+ AttachedSurfaceControl getRootSurfaceControl() {
return mViewRootImpl;
}
@@ -30876,7 +30876,9 @@
/**
* Called when the content from {@link View#onCreateViewTranslationRequest} had been translated
- * by the TranslationService.
+ * by the TranslationService. The {@link ViewTranslationResponse} should be saved here so that
+ * the {@link ViewTranslationResponse} can be used to display the translation when the system
+ * calls {@link ViewTranslationCallback#onShowTranslation}.
*
* <p> The default implementation will set the ViewTranslationResponse that can be get from
* {@link View#getViewTranslationResponse}. </p>
@@ -30929,7 +30931,7 @@
* information, e.g. source spec, target spec.
* @param requests fill in with {@link ViewTranslationRequest}s for translation purpose.
*/
- public void dispatchRequestTranslation(@NonNull Map<AutofillId, long[]> viewIds,
+ public void dispatchCreateViewTranslationRequest(@NonNull Map<AutofillId, long[]> viewIds,
@NonNull @DataFormat int[] supportedFormats,
@NonNull TranslationCapability capability,
@NonNull List<ViewTranslationRequest> requests) {
@@ -31043,17 +31045,17 @@
}
/**
- * @return The {@link android.view.ViewRoot} interface for this View. This will only
- * return a non-null value when called between {@link #onAttachedToWindow} and
- * {@link #onDetachedFromWindow}.
- *
- * The ViewRoot itself is not a View, it is just the interface to the windowing-system
- * object that contains the entire view hierarchy. For the root View of a given hierarchy
- * see {@link #getRootView}.
+ * The AttachedSurfaceControl itself is not a View, it is just the interface to the
+ * windowing-system object that contains the entire view hierarchy.
+ * For the root View of a given hierarchy see {@link #getRootView}.
+
+ * @return The {@link android.view.AttachedSurfaceControl} interface for this View.
+ * This will only return a non-null value when called between {@link #onAttachedToWindow}
+ * and {@link #onDetachedFromWindow}.
*/
- public @Nullable ViewRoot getViewRoot() {
+ public @Nullable AttachedSurfaceControl getRootSurfaceControl() {
if (mAttachInfo != null) {
- return mAttachInfo.getViewRoot();
+ return mAttachInfo.getRootSurfaceControl();
}
return null;
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a02281b..4647f47 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -9275,21 +9275,23 @@
/**
* {@inheritDoc}
*
- * The implementation calls {@link #dispatchRequestTranslation} for all the child views.
+ * The implementation calls {@link #dispatchCreateViewTranslationRequest} for all the child
+ * views.
*/
@Override
- public void dispatchRequestTranslation(@NonNull Map<AutofillId, long[]> viewIds,
+ public void dispatchCreateViewTranslationRequest(@NonNull Map<AutofillId, long[]> viewIds,
@NonNull @DataFormat int[] supportedFormats,
@Nullable TranslationCapability capability,
@NonNull List<ViewTranslationRequest> requests) {
- super.dispatchRequestTranslation(viewIds, supportedFormats, capability, requests);
+ super.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability, requests);
final int childCount = getChildCount();
if (childCount == 0) {
return;
}
for (int i = 0; i < childCount; ++i) {
final View child = getChildAt(i);
- child.dispatchRequestTranslation(viewIds, supportedFormats, capability, requests);
+ child.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability,
+ requests);
}
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a06f193..4d1c666 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -224,7 +224,8 @@
*/
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
- View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, ViewRoot {
+ View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
+ AttachedSurfaceControl {
private static final String TAG = "ViewRootImpl";
private static final boolean DBG = false;
private static final boolean LOCAL_LOGV = false;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 52a09701..5fb4e70 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2722,11 +2722,12 @@
/**
* This will be null before a content view is added, e.g. via
- * {@link #setContentView} or {@link #addContentView}.
+ * {@link #setContentView} or {@link #addContentView}. See
+ * {@link android.view.View#getRootSurfaceControl}.
*
- * @return The {@link android.view.ViewRoot} interface for this Window
+ * @return The {@link android.view.AttachedSurfaceControl} interface for this Window
*/
- public @Nullable ViewRoot getViewRoot() {
+ public @Nullable AttachedSurfaceControl getRootSurfaceControl() {
return null;
}
}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 8a2b629..e0526f8 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -466,8 +466,8 @@
// traverse the hierarchy to collect ViewTranslationRequests
for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
View rootView = roots.get(rootNum).getView();
- rootView.dispatchRequestTranslation(viewIds, supportedFormats, capability,
- requests);
+ rootView.dispatchCreateViewTranslationRequest(viewIds, supportedFormats,
+ capability, requests);
}
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
UiTranslationController::sendTranslationRequest,
diff --git a/core/java/android/view/translation/UiTranslationSpec.java b/core/java/android/view/translation/UiTranslationSpec.java
index b43dbce..3d1ebfd 100644
--- a/core/java/android/view/translation/UiTranslationSpec.java
+++ b/core/java/android/view/translation/UiTranslationSpec.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.widget.TextView;
import com.android.internal.util.DataClass;
@@ -35,36 +36,38 @@
public final class UiTranslationSpec implements Parcelable {
/**
- * Whether the original content of the view should be directly modified to include padding that
- * makes it the same size as the translated content. Defaults to {@code false}.
+ * Whether the original content of the view should be made to appear as if it is the same size
+ * as the translated content. Defaults to {@code false}.
* <p>
- * For {@link android.widget.TextView}, the system does not directly modify the original text,
- * rather changes the displayed content using a
- * {@link android.text.method.TransformationMethod}.
- * This can cause issues in apps that do not account for TransformationMethods. For example, an
- * app using DynamicLayout may use the calculated text offsets to operate on the original text,
- * but this can be problematic when the layout was calculated on translated text with a
- * different length.
+ * For {@link TextView}, the system does not directly modify the original text, rather
+ * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+ * can cause issues in apps that do not account for length-changing TransformationMethods. For
+ * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+ * original text, but this can cause crashes when the layout was calculated on translated text
+ * with a different length.
* <p>
- * If this is {@code true}, for a TextView the default implementation will append spaces to the
- * text to make the length the same as the translated text.
+ * If this is {@code true}, for a TextView the default implementation appends spaces to the
+ * result of {@link TextView#getText()} to make the length the same as the translated text.
+ * <p>
+ * This only affects apps with target SDK R or lower.
*/
private boolean mShouldPadContentForCompat = false;
/**
- * Whether the original content of the view should be directly modified to include padding that
- * makes it the same size as the translated content.
+ * Whether the original content of the view should be made to appear as if it is the same size
+ * as the translated content.
* <p>
- * For {@link android.widget.TextView}, the system does not directly modify the original text,
- * rather changes the displayed content using a
- * {@link android.text.method.TransformationMethod}.
- * This can cause issues in apps that do not account for TransformationMethods. For example, an
- * app using DynamicLayout may use the calculated text offsets to operate on the original text,
- * but this can be problematic when the layout was calculated on translated text with a
- * different length.
+ * For {@link TextView}, the system does not directly modify the original text, rather
+ * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+ * can cause issues in apps that do not account for length-changing TransformationMethods. For
+ * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+ * original text, but this can cause crashes when the layout was calculated on translated text
+ * with a different length.
* <p>
- * If this is {@code true}, for a TextView the default implementation will append spaces to the
- * text to make the length the same as the translated text.
+ * If this is {@code true}, for a TextView the default implementation appends spaces to the
+ * result of {@link TextView#getText()} to make the length the same as the translated text.
+ * <p>
+ * This only affects apps with target SDK R or lower.
*/
public boolean shouldPadContentForCompat() {
return mShouldPadContentForCompat;
@@ -190,19 +193,20 @@
}
/**
- * Whether the original content of the view should be directly modified to include padding that
- * makes it the same size as the translated content. Defaults to {@code false}.
+ * Whether the original content of the view should be made to appear as if it is the same size
+ * as the translated content. Defaults to {@code false}.
* <p>
- * For {@link android.widget.TextView}, the system does not directly modify the original text,
- * rather changes the displayed content using a
- * {@link android.text.method.TransformationMethod}.
- * This can cause issues in apps that do not account for TransformationMethods. For example, an
- * app using DynamicLayout may use the calculated text offsets to operate on the original text,
- * but this can be problematic when the layout was calculated on translated text with a
- * different length.
+ * For {@link TextView}, the system does not directly modify the original text, rather
+ * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+ * can cause issues in apps that do not account for length-changing TransformationMethods. For
+ * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+ * original text, but this can cause crashes when the layout was calculated on translated text
+ * with a different length.
* <p>
- * If this is {@code true}, for a TextView the default implementation will append spaces to the
- * text to make the length the same as the translated text.
+ * If this is {@code true}, for a TextView the default implementation appends spaces to the
+ * result of {@link TextView#getText()} to make the length the same as the translated text.
+ * <p>
+ * This only affects apps with target SDK R or lower.
*/
@DataClass.Generated.Member
public @NonNull Builder setShouldPadContentForCompat(boolean value) {
@@ -234,7 +238,7 @@
}
@DataClass.Generated(
- time = 1619034161701L,
+ time = 1620790033058L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/UiTranslationSpec.java",
inputSignatures = "private boolean mShouldPadContentForCompat\npublic boolean shouldPadContentForCompat()\nclass UiTranslationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genToString=true)")
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a1d4822..0bbaac0f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -60,11 +60,13 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inspector.InspectableProperty;
import android.view.textclassifier.TextClassifier;
+import android.view.translation.TranslationCapability;
import android.view.translation.TranslationSpec.DataFormat;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
@@ -2869,6 +2871,16 @@
}
@Override
+ public void dispatchCreateViewTranslationRequest(@NonNull Map<AutofillId, long[]> viewIds,
+ @NonNull @DataFormat int[] supportedFormats,
+ @Nullable TranslationCapability capability,
+ @NonNull List<ViewTranslationRequest> requests) {
+ super.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability, requests);
+ mProvider.getViewDelegate().dispatchCreateViewTranslationRequest(viewIds, supportedFormats,
+ capability, requests);
+ }
+
+ @Override
public void onVirtualViewTranslationResponses(
@NonNull LongSparseArray<ViewTranslationResponse> response) {
mProvider.getViewDelegate().onVirtualViewTranslationResponses(response);
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 8d996ee..f9f823b 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -45,10 +45,12 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.textclassifier.TextClassifier;
+import android.view.translation.TranslationCapability;
import android.view.translation.TranslationSpec.DataFormat;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
@@ -59,6 +61,7 @@
import java.io.BufferedWriter;
import java.io.File;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -377,6 +380,14 @@
LongSparseArray<ViewTranslationResponse> response) {
}
+ default void dispatchCreateViewTranslationRequest(
+ @NonNull @SuppressWarnings("unused") Map<AutofillId, long[]> viewIds,
+ @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats,
+ @Nullable @SuppressWarnings("unused") TranslationCapability capability,
+ @NonNull @SuppressWarnings("unused") List<ViewTranslationRequest> requests) {
+
+ }
+
public AccessibilityNodeProvider getAccessibilityNodeProvider();
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 49a3e29..14fd4c2 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -86,6 +86,11 @@
public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "ReduceBrightColors");
+ // The component name for the sub setting of Accessibility button in Accessibility settings
+ public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "AccessibilityButton");
+
+
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index a6dc4e0..0ea299d 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -70,12 +70,12 @@
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.AndroidRuntimeException;
+import android.view.AttachedSurfaceControl;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
-import android.view.ViewRoot;
import android.view.ContextThemeWrapper;
import android.view.CrossWindowBlurListeners;
import android.view.Gravity;
@@ -3984,7 +3984,7 @@
}
@Override
- public ViewRoot getViewRoot() {
+ public AttachedSurfaceControl getRootSurfaceControl() {
return getViewRootImplOrNull();
}
}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index 42fb3f4..72b57ab 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -33,6 +33,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
/**
* Tracks the measured charge consumption of various subsystems according to their
@@ -96,8 +97,10 @@
* supportedStandardBuckets must be of size {@link #NUMBER_STANDARD_POWER_BUCKETS}.
* numCustomBuckets >= 0 is the number of (non-standard) custom power buckets on the device.
*/
- public MeasuredEnergyStats(boolean[] supportedStandardBuckets, String[] customBucketNames) {
- final int numTotalBuckets = NUMBER_STANDARD_POWER_BUCKETS + customBucketNames.length;
+ public MeasuredEnergyStats(@NonNull boolean[] supportedStandardBuckets,
+ @Nullable String[] customBucketNames) {
+ mCustomBucketNames = customBucketNames == null ? new String[0] : customBucketNames;
+ final int numTotalBuckets = NUMBER_STANDARD_POWER_BUCKETS + mCustomBucketNames.length;
mAccumulatedChargeMicroCoulomb = new long[numTotalBuckets];
// Initialize to all zeros where supported, otherwise POWER_DATA_UNAVAILABLE.
// All custom buckets are, by definition, supported, so their values stay at 0.
@@ -106,7 +109,6 @@
mAccumulatedChargeMicroCoulomb[stdBucket] = POWER_DATA_UNAVAILABLE;
}
}
- mCustomBucketNames = customBucketNames;
}
/**
@@ -431,14 +433,22 @@
/** Check if the supported power buckets are precisely those given. */
public boolean isSupportEqualTo(
- @NonNull boolean[] queriedStandardBuckets, String[] customBucketNames) {
+ @NonNull boolean[] queriedStandardBuckets, @Nullable String[] customBucketNames) {
+ if (customBucketNames == null) {
+ //In practice customBucketNames should never be null, but sanitize it just to be sure.
+ customBucketNames = new String[0];
+ }
final int numBuckets = getNumberOfIndices();
- // TODO(b/178504428): Detect whether custom buckets have changed qualitatively, not just
- // quantitatively, and treat as mismatch if so.
- if (numBuckets != NUMBER_STANDARD_POWER_BUCKETS + customBucketNames.length) {
+ final int numCustomBuckets = customBucketNames == null ? 0 : customBucketNames.length;
+ if (numBuckets != NUMBER_STANDARD_POWER_BUCKETS + numCustomBuckets) {
return false;
}
+
+ if (!Arrays.equals(mCustomBucketNames, customBucketNames)) {
+ return false;
+ }
+
for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_POWER_BUCKETS; stdBucket++) {
if (isStandardBucketSupported(stdBucket) != queriedStandardBuckets[stdBucket]) {
return false;
diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
index 058a921..4460e4a 100644
--- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
+++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
@@ -27,9 +27,7 @@
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.RemotableViewMethod;
-import android.view.ViewGroup;
import android.widget.Button;
-import android.widget.LinearLayout;
import android.widget.RemoteViews;
import com.android.internal.R;
@@ -42,8 +40,6 @@
@RemoteViews.RemoteView
public class EmphasizedNotificationButton extends Button {
private final RippleDrawable mRipple;
- private final int mStrokeWidth;
- private final int mStrokeColor;
private boolean mPriority;
public EmphasizedNotificationButton(Context context) {
@@ -63,9 +59,6 @@
super(context, attrs, defStyleAttr, defStyleRes);
DrawableWrapper background = (DrawableWrapper) getBackground().mutate();
mRipple = (RippleDrawable) background.getDrawable();
- mStrokeWidth = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.emphasized_button_stroke_width);
- mStrokeColor = getContext().getColor(com.android.internal.R.color.material_grey_300);
mRipple.mutate();
}
@@ -82,13 +75,6 @@
invalidate();
}
- @RemotableViewMethod
- public void setHasStroke(boolean hasStroke) {
- GradientDrawable inner = (GradientDrawable) mRipple.getDrawable(0);
- inner.setStroke(hasStroke ? mStrokeWidth : 0, mStrokeColor);
- invalidate();
- }
-
/**
* Sets an image icon which will have its size constrained and will be set to the same color as
* the text. Must be called after {@link #setTextColor(int)} for the latter to work.
@@ -121,18 +107,13 @@
}
/**
- * Changes the LayoutParams.width to WRAP_CONTENT, with the argument representing if this view
- * is a priority over its peers (which affects weight).
+ * Sets whether this view is a priority over its peers (which affects width).
+ * Specifically, this is used by {@link NotificationActionListLayout} to give this view width
+ * priority ahead of user-defined buttons when allocating horizontal space.
*/
@RemotableViewMethod
- public void setWrapModePriority(boolean priority) {
+ public void setIsPriority(boolean priority) {
mPriority = priority;
- ViewGroup.LayoutParams layoutParams = getLayoutParams();
- layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
- if (layoutParams instanceof LinearLayout.LayoutParams) {
- ((LinearLayout.LayoutParams) layoutParams).weight = 0;
- }
- setLayoutParams(layoutParams);
}
/**
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 8e6497b..a4d6a60 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -17,11 +17,11 @@
package com.android.internal.widget;
import android.annotation.DimenRes;
+import android.app.Notification;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
-import android.util.Pair;
import android.view.Gravity;
import android.view.RemotableViewMethod;
import android.view.View;
@@ -43,10 +43,9 @@
private final int mGravity;
private int mTotalWidth = 0;
private int mExtraStartPadding = 0;
- private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>();
+ private ArrayList<TextViewInfo> mMeasureOrderTextViews = new ArrayList<>();
private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
private boolean mEmphasizedMode;
- private boolean mPrioritizedWrapMode;
private int mDefaultPaddingBottom;
private int mDefaultPaddingTop;
private int mEmphasizedHeight;
@@ -70,16 +69,18 @@
ta.recycle();
}
+ private static boolean isPriority(View actionView) {
+ return actionView instanceof EmphasizedNotificationButton
+ && ((EmphasizedNotificationButton) actionView).isPriority();
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mEmphasizedMode && !mPrioritizedWrapMode) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- return;
- }
final int N = getChildCount();
int textViews = 0;
int otherViews = 0;
int notGoneChildren = 0;
+ int priorityChildren = 0;
for (int i = 0; i < N; i++) {
View c = getChildAt(i);
@@ -90,6 +91,9 @@
}
if (c.getVisibility() != GONE) {
notGoneChildren++;
+ if (isPriority(c)) {
+ priorityChildren++;
+ }
}
}
@@ -103,9 +107,9 @@
if (!needRebuild) {
final int size = mMeasureOrderTextViews.size();
for (int i = 0; i < size; i++) {
- Pair<Integer, TextView> pair = mMeasureOrderTextViews.get(i);
- if (pair.first != pair.second.getText().length()) {
+ if (mMeasureOrderTextViews.get(i).needsRebuild()) {
needRebuild = true;
+ break;
}
}
}
@@ -122,14 +126,19 @@
int usedWidth = 0;
int measuredChildren = 0;
+ int measuredPriorityChildren = 0;
for (int i = 0; i < N; i++) {
// Measure shortest children first. To avoid measuring twice, we approximate by looking
// at the text length.
- View c;
+ final boolean isPriority;
+ final View c;
if (i < otherSize) {
c = mMeasureOrderOther.get(i);
+ isPriority = false;
} else {
- c = mMeasureOrderTextViews.get(i - otherSize).second;
+ TextViewInfo info = mMeasureOrderTextViews.get(i - otherSize);
+ c = info.mTextView;
+ isPriority = info.mIsPriority;
}
if (c.getVisibility() == GONE) {
continue;
@@ -143,7 +152,18 @@
// measure in the order of (approx.) size, a large view can still take more than its
// share if the others are small.
int availableWidth = innerWidth - usedWidth;
- int maxWidthForChild = availableWidth / (notGoneChildren - measuredChildren);
+ int unmeasuredChildren = notGoneChildren - measuredChildren;
+ int maxWidthForChild = availableWidth / unmeasuredChildren;
+ if (isPriority) {
+ // Priority children get a larger maximum share of the total space:
+ // maximum priority share = (nPriority + 1) / (MAX + 1)
+ int unmeasuredPriorityChildren = priorityChildren - measuredPriorityChildren;
+ int unmeasuredOtherChildren = unmeasuredChildren - unmeasuredPriorityChildren;
+ int widthReservedForOtherChildren = innerWidth * unmeasuredOtherChildren
+ / (Notification.MAX_ACTION_BUTTONS + 1);
+ int widthAvailableForPriority = availableWidth - widthReservedForOtherChildren;
+ maxWidthForChild = widthAvailableForPriority / unmeasuredPriorityChildren;
+ }
usedWidthForChild = innerWidth - maxWidthForChild;
}
@@ -153,6 +173,9 @@
usedWidth += c.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
measuredChildren++;
+ if (isPriority) {
+ measuredPriorityChildren++;
+ }
}
int collapsibleIndent = mCollapsibleIndentDimen == 0 ? 0
@@ -175,13 +198,8 @@
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View c = getChildAt(i);
- if (c instanceof EmphasizedNotificationButton
- && ((EmphasizedNotificationButton) c).isPriority()) {
- // add with 0 length to ensure that this view is measured before others.
- mMeasureOrderTextViews.add(Pair.create(0, (TextView) c));
- } else if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
- mMeasureOrderTextViews.add(Pair.create(((TextView) c).getText().length(),
- (TextView)c));
+ if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
+ mMeasureOrderTextViews.add(new TextViewInfo((TextView) c));
} else {
mMeasureOrderOther.add(c);
}
@@ -213,10 +231,6 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (mEmphasizedMode && !mPrioritizedWrapMode) {
- super.onLayout(changed, left, top, right, bottom);
- return;
- }
final boolean isLayoutRtl = isLayoutRtl();
final int paddingTop = mPaddingTop;
final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
@@ -293,16 +307,6 @@
}
/**
- * When used with emphasizedMode, changes the button sizing behavior to prioritize certain
- * buttons (which are system generated) to not scrunch, and leave the remaining space for
- * custom actions.
- */
- @RemotableViewMethod
- public void setPrioritizedWrapMode(boolean prioritizedWrapMode) {
- mPrioritizedWrapMode = prioritizedWrapMode;
- }
-
- /**
* When buttons are in wrap mode, this is a padding that will be applied at the start of the
* layout of the actions, but only when those actions would fit with the entire padding
* visible. Otherwise, this padding will be omitted entirely.
@@ -353,6 +357,28 @@
return 0;
}
- public static final Comparator<Pair<Integer, TextView>> MEASURE_ORDER_COMPARATOR
- = (a, b) -> a.first.compareTo(b.first);
+ public static final Comparator<TextViewInfo> MEASURE_ORDER_COMPARATOR = (a, b) -> {
+ int priorityComparison = -Boolean.compare(a.mIsPriority, b.mIsPriority);
+ return priorityComparison != 0
+ ? priorityComparison
+ : Integer.compare(a.mTextLength, b.mTextLength);
+ };
+
+ private static final class TextViewInfo {
+ final boolean mIsPriority;
+ final int mTextLength;
+ final TextView mTextView;
+
+ TextViewInfo(TextView textView) {
+ this.mIsPriority = isPriority(textView);
+ this.mTextLength = textView.getText().length();
+ this.mTextView = textView;
+ }
+
+ boolean needsRebuild() {
+ return mTextView.getText().length() != mTextLength
+ || isPriority(mTextView) != mIsPriority;
+ }
+ }
+
}
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 315ca2e..5c9999d 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -270,7 +270,28 @@
metadataSrc->unlock(metaBuffer);
return;
}
- metadataDst->update(entry.tag, entry.data.u8, entry.count);
+ switch (entry.type) {
+ case TYPE_BYTE:
+ metadataDst->update(entry.tag, entry.data.u8, entry.count);
+ break;
+ case TYPE_INT32:
+ metadataDst->update(entry.tag, entry.data.i32, entry.count);
+ break;
+ case TYPE_FLOAT:
+ metadataDst->update(entry.tag, entry.data.f, entry.count);
+ break;
+ case TYPE_INT64:
+ metadataDst->update(entry.tag, entry.data.i64, entry.count);
+ break;
+ case TYPE_DOUBLE:
+ metadataDst->update(entry.tag, entry.data.d, entry.count);
+ break;
+ case TYPE_RATIONAL:
+ metadataDst->update(entry.tag, entry.data.r, entry.count);
+ break;
+ default:
+ ALOGE("%s: Unsupported tag type: %d!", __func__, entry.type);
+ }
}
metadataSrc->unlock(metaBuffer);
}
diff --git a/core/res/Android.bp b/core/res/Android.bp
index b988b5a..6063062 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -100,8 +100,7 @@
+ "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && "
+ "mkdir -p $$RES_DIR/values && "
+ "cp $${INPUTS[1]} $$RES_DIR/values && "
- + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR && "
- + "cp $(out) ."
+ + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR"
}
android_app {
diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml
index ad68054..63707ab 100644
--- a/core/res/res/drawable/btn_notification_emphasized.xml
+++ b/core/res/res/drawable/btn_notification_emphasized.xml
@@ -24,13 +24,11 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="@dimen/notification_action_button_radius" />
- <padding android:left="@dimen/button_padding_horizontal_material"
+ <padding android:left="12dp"
android:top="@dimen/button_padding_vertical_material"
- android:right="@dimen/button_padding_horizontal_material"
+ android:right="12dp"
android:bottom="@dimen/button_padding_vertical_material" />
<solid android:color="@color/white" />
- <stroke android:width="@dimen/emphasized_button_stroke_width"
- android:color="@color/material_grey_300"/>
</shape>
</item>
</ripple>
diff --git a/core/res/res/layout/notification_material_action_emphasized.xml b/core/res/res/layout/notification_material_action_emphasized.xml
index cd1f1ab..ea84185 100644
--- a/core/res/res/layout/notification_material_action_emphasized.xml
+++ b/core/res/res/layout/notification_material_action_emphasized.xml
@@ -18,10 +18,9 @@
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/NotificationEmphasizedAction"
android:id="@+id/action0"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="12dp"
- android:layout_weight="1"
android:drawablePadding="6dp"
android:gravity="center"
android:textColor="@color/notification_default_color"
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4f90a17..d334306 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -224,14 +224,14 @@
<!-- The margin on the end of the top-line content views (accommodates the expander) -->
<dimen name="notification_heading_margin_end">56dp</dimen>
- <!-- The height of the notification action list -->
+ <!-- The total height of the notification action list -->
<dimen name="notification_action_list_height">60dp</dimen>
<!-- The margin of the notification action list at the top -->
<dimen name="notification_action_list_margin_top">0dp</dimen>
- <!-- The height of the notification action list -->
- <dimen name="notification_action_emphasized_height">48dp</dimen>
+ <!-- The visual height of the emphasized notification action -->
+ <dimen name="notification_action_emphasized_height">36dp</dimen>
<!-- The padding of the actions in non-conversation layout. For conversations, the analogous
value is calculated in ConversationLayout#updateActionListPadding() -->
@@ -252,7 +252,7 @@
<dimen name="notification_actions_icon_drawable_size">20dp</dimen>
<!-- The corner radius if the emphasized action buttons in a notification -->
- <dimen name="notification_action_button_radius">8dp</dimen>
+ <dimen name="notification_action_button_radius">18dp</dimen>
<!-- Size of the stroke with for the emphasized notification button style -->
<dimen name="emphasized_button_stroke_width">1dp</dimen>
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
index 9e88373..a613e77 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
@@ -16,18 +16,12 @@
package android.app.appsearch;
-
import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
-import com.google.common.collect.ImmutableList;
-
import org.junit.Test;
-import java.util.List;
-import java.util.Map;
-
public class SearchSpecTest {
@Test
@@ -63,32 +57,4 @@
assertThat(bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD))
.isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
}
-
- @Test
- public void testGetProjectionTypePropertyMasks() {
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
- .addProjection("TypeA", ImmutableList.of("field1", "field2.subfield2"))
- .addProjection("TypeB", ImmutableList.of("field7"))
- .addProjection("TypeC", ImmutableList.of())
- .build();
-
- Map<String, List<String>> typePropertyPathMap = searchSpec.getProjections();
- assertThat(typePropertyPathMap.keySet()).containsExactly("TypeA", "TypeB", "TypeC");
- assertThat(typePropertyPathMap.get("TypeA")).containsExactly("field1", "field2.subfield2");
- assertThat(typePropertyPathMap.get("TypeB")).containsExactly("field7");
- assertThat(typePropertyPathMap.get("TypeC")).isEmpty();
- }
-
- @Test
- public void testGetRankingStrategy() {
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
- .setRankingStrategy(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE)
- .build();
- assertThat(searchSpec.getRankingStrategy())
- .isEqualTo(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE);
- }
}
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 46dbe0f..e7ee9dc 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -166,7 +166,7 @@
Configuration newConfig = new Configuration();
newConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
- mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null);
+ mResourcesManager.applyConfigurationToResources(newConfig, null);
final Configuration expectedConfig = new Configuration();
expectedConfig.setToDefaults();
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index 2d894f5..a70033b 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -499,4 +499,84 @@
assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_DOZE));
assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_DOZE_SUSPEND));
}
+
+ /** Test MeasuredEnergyStats#isSupportEqualTo */
+ @Test
+ public void testIsSupportEqualTo() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+ Arrays.fill(supportedStandardBuckets, true);
+ final String[] customBucketNames = {"A", "B"};
+
+ final MeasuredEnergyStats stats =
+ new MeasuredEnergyStats(supportedStandardBuckets.clone(),
+ customBucketNames.clone());
+
+ assertTrue(
+ "All standard and custom bucket supports match",
+ stats.isSupportEqualTo(supportedStandardBuckets, customBucketNames));
+
+ boolean[] differentSupportedStandardBuckets = supportedStandardBuckets.clone();
+ differentSupportedStandardBuckets[0] = !differentSupportedStandardBuckets[0];
+ assertFalse(
+ "Standard bucket support mismatch",
+ stats.isSupportEqualTo(differentSupportedStandardBuckets, customBucketNames));
+
+ assertFalse(
+ "Custom bucket support mismatch",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"C", "B"}));
+
+ assertFalse(
+ "Fewer custom buckets supported",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A"}));
+
+ assertFalse(
+ "More custom bucket supported",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B", "C"}));
+
+ assertFalse(
+ "Custom bucket support order changed",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"B", "A"}));
+ }
+
+ /** Test MeasuredEnergyStats#isSupportEqualTo when holding a null array of custom buckets */
+ @Test
+ public void testIsSupportEqualTo_nullCustomBuckets() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+
+ final MeasuredEnergyStats stats =
+ new MeasuredEnergyStats(supportedStandardBuckets.clone(), null);
+
+ assertTrue(
+ "Null custom bucket name lists should match",
+ stats.isSupportEqualTo(supportedStandardBuckets, null));
+
+ assertTrue(
+ "Null and empty custom buckets should match",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[0]));
+
+ assertFalse(
+ "Null custom buckets should not match populated list",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B"}));
+ }
+
+ /** Test MeasuredEnergyStats#isSupportEqualTo when holding an empty array of custom buckets */
+ @Test
+ public void testIsSupportEqualTo_emptyCustomBuckets() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+
+ final MeasuredEnergyStats stats =
+ new MeasuredEnergyStats(supportedStandardBuckets.clone(), new String[0]);
+
+ assertTrue(
+ "Empty custom buckets should match",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[0]));
+
+ assertTrue(
+ "Empty and null custom buckets should match",
+ stats.isSupportEqualTo(supportedStandardBuckets, null));
+
+ assertFalse(
+ "Empty custom buckets should not match populated list",
+ stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B"}));
+ }
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index e222570..51bf6d53 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -255,6 +255,12 @@
| FILTER_BITMAP_FLAG;
/**
+ * These flags are always set on a reset paint or a new paint instantiated using
+ * {@link #Paint()}.
+ */
+ private static final int DEFAULT_PAINT_FLAGS = ANTI_ALIAS_FLAG | DITHER_FLAG;
+
+ /**
* Font hinter option that disables font hinting.
*
* @see #setHinting(int)
@@ -570,10 +576,13 @@
* accelerated drawing always acts as if {@link #FILTER_BITMAP_FLAG} is set.
* On devices running {@link Build.VERSION_CODES#Q} and above,
* {@code FILTER_BITMAP_FLAG} is set by this constructor, and it can be
- * cleared with {@link #setFlags} or {@link #setFilterBitmap}.</p>
+ * cleared with {@link #setFlags} or {@link #setFilterBitmap}.
+ * On devices running {@link Build.VERSION_CODES#S} and above, {@code ANTI_ALIAS_FLAG} and
+ * {@code DITHER_FLAG} are set by this constructor, and they can be cleared with
+ * {@link #setFlags} or {@link #setAntiAlias} and {@link #setDither}, respectively.</p>
*/
public Paint() {
- this(0);
+ this(DEFAULT_PAINT_FLAGS);
}
/**
@@ -618,7 +627,7 @@
/** Restores the paint to its default settings. */
public void reset() {
nReset(mNativePaint);
- setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
+ setFlags(HIDDEN_DEFAULT_PAINT_FLAGS | DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index fd6f0ad9..1f098c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -66,7 +66,7 @@
// For example, an icon with the foreground 108*108 opaque pixels and it's background
// also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon.
private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f);
- private static final float NO_BACKGROUND_SCALE = 1.3f;
+ private static final float NO_BACKGROUND_SCALE = 192f / 160;
private final Context mContext;
private final IconProvider mIconProvider;
@@ -283,7 +283,8 @@
} else {
final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
- final int scaledIconDpi = (int) (0.5f + iconScale * densityDpi);
+ final int scaledIconDpi =
+ (int) (0.5f + iconScale * densityDpi * NO_BACKGROUND_SCALE);
iconDrawable = mIconProvider.getIcon(mActivityInfo, scaledIconDpi);
if (iconDrawable == null) {
iconDrawable = mContext.getPackageManager().getDefaultActivityIcon();
@@ -356,7 +357,7 @@
Slog.d(TAG, "makeSplashScreenContentView: choose fg icon");
}
// Reference AdaptiveIcon description, outer is 108 and inner is 72, so we
- // should enlarge the size 108/72 if we only draw adaptiveIcon's foreground.
+ // scale by 192/160 if we only draw adaptiveIcon's foreground.
final float noBgScale =
foreIconTester.nonTransparentRatio() < ENLARGE_FOREGROUND_ICON_THRESHOLD
? NO_BACKGROUND_SCALE : 1f;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 7d061fb..320106a 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -226,9 +226,9 @@
// staging aliases can only be defined by the framework package (which is not a shared
// library), the compile-time package id of the framework is the same across all packages
// that compile against the framework.
- for (const auto& package : iter2->packages_) {
+ for (const auto& package : iter->packages_) {
for (const auto& entry : package.loaded_package_->GetAliasResourceIdMap()) {
- iter->dynamic_ref_table->addAlias(entry.first, entry.second);
+ iter2->dynamic_ref_table->addAlias(entry.first, entry.second);
}
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 100bfb6..4658035 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -137,9 +137,10 @@
int mCaptureSequence = 0;
// Multi frame serialization stream and writer used when serializing more than one frame.
+ std::unique_ptr<SkSharingSerialContext> mSerialContext; // Must be declared before any other
+ // serializing member
std::unique_ptr<SkFILEWStream> mOpenMultiPicStream;
sk_sp<SkDocument> mMultiPic;
- std::unique_ptr<SkSharingSerialContext> mSerialContext;
/**
* mRecorder holds the current picture recorder when serializing in either SingleFrameSKP or
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index bba2207..bae1ab5 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -467,11 +467,11 @@
mRenderThread.pushBackFrameCallback(this);
}
-void CanvasContext::draw() {
+nsecs_t CanvasContext::draw() {
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
- return;
+ return 0;
}
}
SkRect dirty;
@@ -486,7 +486,7 @@
std::invoke(func, mFrameNumber);
}
mFrameCompleteCallbacks.clear();
- return;
+ return 0;
}
ScopedActiveContext activeContext(this);
@@ -616,6 +616,7 @@
}
mRenderThread.cacheManager().onFrameCompleted();
+ return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
}
void CanvasContext::reportMetricsWithPresentTime() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index af1ebb2..4f8e4ca 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -127,7 +127,8 @@
void setColorMode(ColorMode mode);
bool makeCurrent();
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
- void draw();
+ // Returns the DequeueBufferDuration.
+ nsecs_t draw();
void destroy();
// IFrameCallback, Choreographer-driven frame callback entry point
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index cb92aa1..8448b87 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -18,6 +18,7 @@
#include <utils/Log.h>
#include <utils/TraceUtils.h>
+#include <algorithm>
#include "../DeferredLayerUpdater.h"
#include "../DisplayList.h"
@@ -91,6 +92,7 @@
void DrawFrameTask::run() {
const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
+ nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
bool canUnblockUiThread;
bool canDrawThisFrame;
@@ -124,8 +126,9 @@
[callback, frameNr = context->getFrameNumber()]() { callback(frameNr); });
}
+ nsecs_t dequeueBufferDuration = 0;
if (CC_LIKELY(canDrawThisFrame)) {
- context->draw();
+ dequeueBufferDuration = context->draw();
} else {
// wait on fences so tasks don't overlap next frame
context->waitOnFences();
@@ -149,10 +152,14 @@
mUpdateTargetWorkDuration(targetWorkDuration);
}
int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
- if (frameDuration > kSanityCheckLowerBound && frameDuration < kSanityCheckUpperBound) {
- mReportActualWorkDuration(frameDuration);
+ int64_t actualDuration = frameDuration -
+ (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+ dequeueBufferDuration;
+ if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
+ mReportActualWorkDuration(actualDuration);
}
}
+ mLastDequeueBufferDuration = dequeueBufferDuration;
}
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 3bb574a..2455ea8 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -110,6 +110,7 @@
std::function<void(int64_t)> mFrameCallback;
std::function<void(int64_t)> mFrameCompleteCallback;
+ nsecs_t mLastDequeueBufferDuration = 0;
nsecs_t mLastTargetWorkDuration = 0;
std::function<void(int64_t)> mUpdateTargetWorkDuration;
std::function<void(int64_t)> mReportActualWorkDuration;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 39b7922..5f6fc17 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -131,6 +131,9 @@
public static final int SAMPLE_RATE_HZ_MIN = native_getMinSampleRate();
private static native int native_getMinSampleRate();
+ /** @hide */
+ public static final int FCC_24 = 24; // fixed channel count 24; do not change.
+
// Expose only the getter method publicly so we can change it in the future
private static final int NUM_STREAM_TYPES = 12;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index b2b2f8e..23d9532 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1720,9 +1720,10 @@
mChannelCount = 0;
break; // channel index configuration only
}
- if (!isMultichannelConfigSupported(channelConfig)) {
- // input channel configuration features unsupported channels
- throw new IllegalArgumentException("Unsupported channel configuration.");
+ if (!isMultichannelConfigSupported(channelConfig, audioFormat)) {
+ throw new IllegalArgumentException(
+ "Unsupported channel mask configuration " + channelConfig
+ + " for encoding " + audioFormat);
}
mChannelMask = channelConfig;
mChannelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
@@ -1730,13 +1731,17 @@
// check the channel index configuration (if present)
mChannelIndexMask = channelIndexMask;
if (mChannelIndexMask != 0) {
- // restrictive: indexMask could allow up to AUDIO_CHANNEL_BITS_LOG2
- final int indexMask = (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1;
- if ((channelIndexMask & ~indexMask) != 0) {
- throw new IllegalArgumentException("Unsupported channel index configuration "
- + channelIndexMask);
+ // As of S, we accept up to 24 channel index mask.
+ final int fullIndexMask = (1 << AudioSystem.FCC_24) - 1;
+ final int channelIndexCount = Integer.bitCount(channelIndexMask);
+ final boolean accepted = (channelIndexMask & ~fullIndexMask) == 0
+ && (!AudioFormat.isEncodingLinearFrames(audioFormat) // compressed OK
+ || channelIndexCount <= AudioSystem.OUT_CHANNEL_COUNT_MAX); // PCM
+ if (!accepted) {
+ throw new IllegalArgumentException(
+ "Unsupported channel index mask configuration " + channelIndexMask
+ + " for encoding " + audioFormat);
}
- int channelIndexCount = Integer.bitCount(channelIndexMask);
if (mChannelCount == 0) {
mChannelCount = channelIndexCount;
} else if (mChannelCount != channelIndexCount) {
@@ -1789,16 +1794,19 @@
* @param channelConfig the mask to validate
* @return false if the AudioTrack can't be used with such a mask
*/
- private static boolean isMultichannelConfigSupported(int channelConfig) {
+ private static boolean isMultichannelConfigSupported(int channelConfig, int encoding) {
// check for unsupported channels
if ((channelConfig & SUPPORTED_OUT_CHANNELS) != channelConfig) {
loge("Channel configuration features unsupported channels");
return false;
}
final int channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
- if (channelCount > AudioSystem.OUT_CHANNEL_COUNT_MAX) {
- loge("Channel configuration contains too many channels " +
- channelCount + ">" + AudioSystem.OUT_CHANNEL_COUNT_MAX);
+ final int channelCountLimit = AudioFormat.isEncodingLinearFrames(encoding)
+ ? AudioSystem.OUT_CHANNEL_COUNT_MAX // PCM limited to OUT_CHANNEL_COUNT_MAX
+ : AudioSystem.FCC_24; // Compressed limited to 24 channels
+ if (channelCount > channelCountLimit) {
+ loge("Channel configuration contains too many channels for encoding "
+ + encoding + "(" + channelCount + " > " + channelCountLimit + ")");
return false;
}
// check for unsupported multichannel combinations:
@@ -2310,7 +2318,7 @@
channelCount = 2;
break;
default:
- if (!isMultichannelConfigSupported(channelConfig)) {
+ if (!isMultichannelConfigSupported(channelConfig, audioFormat)) {
loge("getMinBufferSize(): Invalid channel configuration.");
return ERROR_BAD_VALUE;
} else {
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index a8c2ea5..93a5444 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -454,28 +454,7 @@
sourceRect.makeInvalid();
}
transaction->setBufferCrop(surfaceControl, sourceRect);
-
- int destW = destRect.width();
- int destH = destRect.height();
- if (destRect.left < 0) {
- destRect.left = 0;
- destRect.right = destW;
- }
- if (destRect.top < 0) {
- destRect.top = 0;
- destRect.bottom = destH;
- }
-
- if (!sourceRect.isEmpty()) {
- float sx = destW / static_cast<float>(sourceRect.width());
- float sy = destH / static_cast<float>(sourceRect.height());
- transaction->setPosition(surfaceControl, destRect.left - (sourceRect.left * sx),
- destRect.top - (sourceRect.top * sy));
- transaction->setMatrix(surfaceControl, sx, 0, 0, sy);
- } else {
- transaction->setPosition(surfaceControl, destRect.left, destRect.top);
- }
-
+ transaction->setDestinationFrame(surfaceControl, destRect);
transaction->setTransform(surfaceControl, transform);
bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 60b0f1e..9fe7929 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -329,7 +329,9 @@
static_cast<minikin::FamilyVariant>(matcher->mFamilyVariant),
1 /* maxRun */);
- const std::shared_ptr<minikin::Font>& font = runs[0].fakedFont.font;
+ const std::shared_ptr<minikin::Font>& font =
+ fc->getBestFont(minikin::U16StringPiece(text, textLength), runs[0], matcher->mFontStyle)
+ .font;
std::unique_ptr<AFont> result = std::make_unique<AFont>();
const android::MinikinFontSkia* minikinFontSkia =
reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get());
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 3d6cc68..614dfc1 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -839,8 +839,17 @@
final int[] originalAdministratorUids = getAdministratorUids();
final TransportInfo originalTransportInfo = getTransportInfo();
clearAll();
- mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS)
- | (1 << TRANSPORT_TEST);
+ if (0 != (originalCapabilities & NET_CAPABILITY_NOT_RESTRICTED)) {
+ // If the test network is not restricted, then it is only allowed to declare some
+ // specific transports. This is to minimize impact on running apps in case an app
+ // run from the shell creates a test a network.
+ mTransportTypes =
+ (originalTransportTypes & UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS)
+ | (1 << TRANSPORT_TEST);
+ } else {
+ // If the test transport is restricted, then it may declare any transport.
+ mTransportTypes = (originalTransportTypes | (1 << TRANSPORT_TEST));
+ }
mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
mNetworkSpecifier = originalSpecifier;
mSignalStrength = originalSignalStrength;
@@ -951,9 +960,10 @@
};
/**
- * Allowed transports on a test network, in addition to TRANSPORT_TEST.
+ * Allowed transports on an unrestricted test network (in addition to TRANSPORT_TEST).
*/
- private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST
+ private static final int UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS =
+ 1 << TRANSPORT_TEST
// Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces
| 1 << TRANSPORT_ETHERNET
// Test VPN networks can be created but their UID ranges must be empty.
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index 784a747..ca69a28 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index 5b7bda4..d7cfb96 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index 784a747..ca69a28 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index 780cb8a..84c3401 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 16a946d..f8cb5d3 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -561,7 +561,20 @@
break;
}
- Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
+ StringBuilder msg = new StringBuilder();
+ msg.append("status: " + statusString + ", cause: " + causeString);
+ if (status == STATUS_IN_PROGRESS) {
+ msg.append(
+ String.format(
+ ", partition name: %s, progress: %d/%d",
+ mCurrentPartitionName,
+ mCurrentPartitionInstalledSize,
+ mCurrentPartitionSize));
+ }
+ if (detail != null) {
+ msg.append(", detail: " + detail);
+ }
+ Log.d(TAG, msg.toString());
if (notifyOnNotificationBar) {
mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 59ea9f0..f18d426 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -320,20 +320,21 @@
}
}
- private void installScratch() throws IOException {
- final long scratchSize = mDynSystem.suggestScratchSize();
+ private void installWritablePartition(final String partitionName, final long partitionSize)
+ throws IOException {
+ Log.d(TAG, "Creating writable partition: " + partitionName + ", size: " + partitionSize);
+
Thread thread = new Thread() {
@Override
public void run() {
mInstallationSession =
- mDynSystem.createPartition("scratch", scratchSize, /* readOnly= */ false);
+ mDynSystem.createPartition(
+ partitionName, partitionSize, /* readOnly= */ false);
}
};
- Log.d(TAG, "Creating partition: scratch, size = " + scratchSize);
thread.start();
-
- Progress progress = new Progress("scratch", scratchSize, mNumInstalledPartitions++);
+ Progress progress = new Progress(partitionName, partitionSize, mNumInstalledPartitions++);
while (thread.isAlive()) {
if (isCancelled()) {
@@ -356,53 +357,22 @@
if (mInstallationSession == null) {
throw new IOException(
- "Failed to start installation with requested size: " + scratchSize);
+ "Failed to start installation with requested size: " + partitionSize);
}
+
// Reset installation session and verify that installation completes successfully.
mInstallationSession = null;
if (!mDynSystem.closePartition()) {
- throw new IOException("Failed to complete partition installation: scratch");
+ throw new IOException("Failed to complete partition installation: " + partitionName);
}
}
+ private void installScratch() throws IOException {
+ installWritablePartition("scratch", mDynSystem.suggestScratchSize());
+ }
+
private void installUserdata() throws IOException {
- Thread thread = new Thread(() -> {
- mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
- });
-
- Log.d(TAG, "Creating partition: userdata, size = " + mUserdataSize);
- thread.start();
-
- Progress progress = new Progress("userdata", mUserdataSize, mNumInstalledPartitions++);
-
- while (thread.isAlive()) {
- if (isCancelled()) {
- return;
- }
-
- final long installedSize = mDynSystem.getInstallationProgress().bytes_processed;
-
- if (installedSize > progress.installedSize + MIN_PROGRESS_TO_PUBLISH) {
- progress.installedSize = installedSize;
- publishProgress(progress);
- }
-
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- // Ignore the error.
- }
- }
-
- if (mInstallationSession == null) {
- throw new IOException(
- "Failed to start installation with requested size: " + mUserdataSize);
- }
- // Reset installation session and verify that installation completes successfully.
- mInstallationSession = null;
- if (!mDynSystem.closePartition()) {
- throw new IOException("Failed to complete partition installation: userdata");
- }
+ installWritablePartition("userdata", mUserdataSize);
}
private void installImages() throws IOException, ImageValidationException {
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 69cee00..a65bf41 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -54,6 +54,7 @@
"SettingsLibAppPreference",
"SettingsLibSearchWidget",
"SettingsLibSettingsSpinner",
+ "SettingsLibIllustrationPreference",
"SettingsLibLayoutPreference",
"SettingsLibMainSwitchPreference",
"SettingsLibActionButtonsPreference",
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
new file mode 100644
index 0000000..f8dd384
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -0,0 +1,23 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SettingsLibIllustrationPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ "lottie",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml
new file mode 100644
index 0000000..120b085
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml b/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml
new file mode 100644
index 0000000..55b3115
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path android:fillColor="#FFFFFF" android:pathData="M24,24m-19,0a19,19 0,1 1,38 0a19,19 0,1 1,-38 0"/>
+ <path android:fillColor="#1A73E8" android:pathData="M20,33l12,-9l-12,-9z"/>
+ <path android:fillColor="#1A73E8" android:pathData="M24,4C12.96,4 4,12.96 4,24s8.96,20 20,20s20,-8.96 20,-20S35.04,4 24,4zM24,40c-8.82,0 -16,-7.18 -16,-16S15.18,8 24,8s16,7.18 16,16S32.82,40 24,40z"/>
+</vector>
diff --git a/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml
new file mode 100644
index 0000000..dd2fa5e
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="@dimen/settingslib_illustration_padding"
+ android:left="@dimen/settingslib_illustration_padding"
+ android:right="@dimen/settingslib_illustration_padding"
+ android:bottom="@dimen/settingslib_illustration_padding">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/settingslib_protection_color"/>
+ <corners android:radius="28dp"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
new file mode 100644
index 0000000..2cbb888
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:background="?android:attr/colorBackground"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <View
+ android:id="@+id/protection_layer"
+ android:layout_width="412dp"
+ android:layout_height="300dp"
+ android:layout_gravity="center"
+ android:padding="@dimen/settingslib_illustration_padding"
+ android:background="@drawable/protection_background"/>
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/lottie_view"
+ android:layout_width="412dp"
+ android:layout_height="300dp"
+ android:layout_gravity="center"
+ android:padding="@dimen/settingslib_illustration_padding"
+ android:importantForAccessibility="no"/>
+
+ <ImageView
+ android:id="@+id/video_play_button"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_gravity="center"
+ android:visibility="gone"
+ android:src="@drawable/ic_gesture_play_button"/>
+
+</FrameLayout>
+
diff --git a/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml
new file mode 100644
index 0000000..71b18a8
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <color name="settingslib_protection_color">@android:color/black</color>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
new file mode 100644
index 0000000..e53a43e
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <color name="settingslib_protection_color">@android:color/white</color>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
new file mode 100644
index 0000000..79562b9
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <!-- Padding of illustration -->
+ <dimen name="settingslib_illustration_padding">16dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
new file mode 100644
index 0000000..90b8a32
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceViewHolder;
+
+import com.airbnb.lottie.LottieAnimationView;
+
+/**
+ * IllustrationPreference is a preference that can play lottie format animation
+ */
+public class IllustrationPreference extends Preference implements OnPreferenceClickListener {
+
+ static final String TAG = "IllustrationPreference";
+ private int mAnimationId;
+ private boolean mIsAnimating;
+ private ImageView mPlayButton;
+ private LottieAnimationView mIllustrationView;
+
+ public IllustrationPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs);
+ }
+
+ public IllustrationPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context, attrs);
+ }
+
+ public IllustrationPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context, attrs);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ if (mAnimationId == 0) {
+ Log.w(TAG, "Invalid illustration resource id.");
+ return;
+ }
+ mPlayButton = (ImageView) holder.findViewById(R.id.video_play_button);
+ mIllustrationView = (LottieAnimationView) holder.findViewById(R.id.lottie_view);
+ mIllustrationView.setAnimation(mAnimationId);
+ mIllustrationView.loop(true);
+ mIllustrationView.playAnimation();
+ updateAnimationStatus(mIsAnimating);
+ setOnPreferenceClickListener(this);
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ mIsAnimating = !isAnimating();
+ updateAnimationStatus(mIsAnimating);
+ return true;
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.mIsAnimating = mIsAnimating;
+ return ss;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ mIsAnimating = ss.mIsAnimating;
+ }
+
+ @VisibleForTesting
+ boolean isAnimating() {
+ return mIllustrationView.isAnimating();
+ }
+
+ private void init(Context context, AttributeSet attrs) {
+ setLayoutResource(R.layout.illustration_preference);
+
+ mIsAnimating = true;
+ if (attrs != null) {
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.LottieAnimationView, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
+ mAnimationId = a.getResourceId(R.styleable.LottieAnimationView_lottie_rawRes, 0);
+ a.recycle();
+ }
+ }
+
+ private void updateAnimationStatus(boolean playAnimation) {
+ if (playAnimation) {
+ mIllustrationView.resumeAnimation();
+ mPlayButton.setVisibility(View.INVISIBLE);
+ } else {
+ mIllustrationView.pauseAnimation();
+ mPlayButton.setVisibility(View.VISIBLE);
+ }
+ }
+
+ static class SavedState extends BaseSavedState {
+ boolean mIsAnimating;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ /**
+ * Constructor called from {@link #CREATOR}
+ */
+ private SavedState(Parcel in) {
+ super(in);
+ mIsAnimating = (Boolean) in.readValue(null);
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeValue(mIsAnimating);
+ }
+
+ @Override
+ public String toString() {
+ return "IllustrationPreference.SavedState{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " mIsAnimating=" + mIsAnimating + "}";
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 0748acd..123c477 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -72,9 +72,7 @@
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- LayoutInflater.from(context).inflate(
- resourceId(context, "layout", "settingslib_main_switch_bar"),
- this);
+ LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this);
if (!BuildCompat.isAtLeastS()) {
final TypedArray a = context.obtainStyledAttributes(
@@ -90,12 +88,10 @@
mFrameView = findViewById(R.id.frame);
mTextView = (TextView) findViewById(R.id.switch_text);
mSwitch = (Switch) findViewById(android.R.id.switch_widget);
- mBackgroundOn = getContext().getDrawable(
- resourceId(context, "drawable", "settingslib_switch_bar_bg_on"));
- mBackgroundOff = getContext().getDrawable(
- resourceId(context, "drawable", "settingslib_switch_bar_bg_off"));
+ mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+ mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off);
mBackgroundDisabled = getContext().getDrawable(
- resourceId(context, "drawable", "settingslib_switch_bar_bg_disabled"));
+ R.drawable.settingslib_switch_bar_bg_disabled);
addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked));
@@ -302,8 +298,4 @@
requestLayout();
}
-
- private int resourceId(Context context, String type, String name) {
- return context.getResources().getIdentifier(name, type, context.getPackageName());
- }
}
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
index 154a0f4..304c343 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -36,6 +36,7 @@
private SettingsSpinnerAdapter mAdapter;
private AdapterView.OnItemSelectedListener mListener;
private int mPosition; //Default 0 for internal shard storage.
+ private boolean mIsClickable = true;
/**
* Perform inflation from XML and apply a class-specific base style.
@@ -50,6 +51,7 @@
public SettingsSpinnerPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setLayoutResource(R.layout.settings_spinner_preference);
+ setSelectable(false);
}
/**
@@ -62,6 +64,7 @@
public SettingsSpinnerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.settings_spinner_preference);
+ setSelectable(false);
}
/**
@@ -98,10 +101,21 @@
notifyChanged();
}
+ /** Set clickable of the spinner. */
+ public void setClickable(boolean isClickable) {
+ if (mIsClickable == isClickable) {
+ return;
+ }
+ mIsClickable = isClickable;
+ notifyChanged();
+ }
+
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.spinner);
+ spinner.setEnabled(mIsClickable);
+ spinner.setClickable(mIsClickable);
spinner.setAdapter(mAdapter);
spinner.setSelection(mPosition);
spinner.setOnItemSelectedListener(mOnSelectedListener);
diff --git a/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
index 17f257d..9130662 100644
--- a/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
+++ b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
@@ -72,9 +72,9 @@
private void init(Context context) {
setLayoutResource(R.layout.preference_two_target);
mSmallIconSize = context.getResources().getDimensionPixelSize(
- resourceId(context, "dimen", "two_target_pref_small_icon_size"));
+ R.dimen.two_target_pref_small_icon_size);
mMediumIconSize = context.getResources().getDimensionPixelSize(
- resourceId(context, "dimen", "two_target_pref_medium_icon_size"));
+ R.dimen.two_target_pref_medium_icon_size);
final int secondTargetResId = getSecondTargetResId();
if (secondTargetResId != 0) {
setWidgetLayoutResource(secondTargetResId);
@@ -116,8 +116,4 @@
protected int getSecondTargetResId() {
return 0;
}
-
- private int resourceId(Context context, String type, String name) {
- return context.getResources().getIdentifier(name, type, context.getPackageName());
- }
}
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml
new file mode 100644
index 0000000..efae569
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.66,7C18.19,5.07 15.14,4 12,4C8.58,4 5.27,5.27 2.7,7.53L12,18.85l6,-7.3v3.15L12,22L0,7.39C2.97,4.08 7.25,2 12,2c4.56,0 8.69,1.92 11.64,5H20.66z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml
new file mode 100644
index 0000000..d50e734
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M23.62,7C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4L12,22l6,-7.3v-3.19l-2.13,2.59C14.74,13.4 13.39,13 12,13s-2.74,0.4 -3.87,1.1L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3H23.62z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml
new file mode 100644
index 0000000..1be297e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M23.62,7C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4L12,22l6,-7.3v-3.19l-0.27,0.33C16.12,10.67 14.09,10 12,10c-2.09,0 -4.12,0.67 -5.73,1.84L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3H23.62z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml
new file mode 100644
index 0000000..738bd5a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M0,7.4L12,22l6,-7.3V8.57C16.21,7.56 14.14,7 12,7C9.2,7 6.53,7.96 4.45,9.62L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3h2.95C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml
new file mode 100644
index 0000000..14d020b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M0,7.4C3,4.1 7.2,2 12,2c4.6,0 8.65,1.93 11.62,5H18v7.7L12,22L0,7.4z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 9889419..4dd3ff1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -187,8 +187,9 @@
}
protected int getIconColorAttr() {
- return (mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED)
- ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
+ final boolean accent = (mWifiEntry.hasInternetAccess()
+ && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED);
+ return accent ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
}
private void updateIcon(boolean showX, int level) {
@@ -267,7 +268,7 @@
}
public Drawable getIcon(boolean showX, int level) {
- return mContext.getDrawable(Utils.getWifiIconResource(showX, level));
+ return mContext.getDrawable(WifiUtils.getInternetIconResource(level, showX));
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 15b146d..6100615 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -36,6 +36,22 @@
private static final int INVALID_RSSI = -127;
+ static final int[] WIFI_PIE = {
+ com.android.internal.R.drawable.ic_wifi_signal_0,
+ com.android.internal.R.drawable.ic_wifi_signal_1,
+ com.android.internal.R.drawable.ic_wifi_signal_2,
+ com.android.internal.R.drawable.ic_wifi_signal_3,
+ com.android.internal.R.drawable.ic_wifi_signal_4
+ };
+
+ static final int[] NO_INTERNET_WIFI_PIE = {
+ R.drawable.ic_no_internet_wifi_signal_0,
+ R.drawable.ic_no_internet_wifi_signal_1,
+ R.drawable.ic_no_internet_wifi_signal_2,
+ R.drawable.ic_no_internet_wifi_signal_3,
+ R.drawable.ic_no_internet_wifi_signal_4
+ };
+
public static String buildLoggingSummary(AccessPoint accessPoint, WifiConfiguration config) {
final StringBuilder summary = new StringBuilder();
final WifiInfo info = accessPoint.getInfo();
@@ -245,6 +261,20 @@
return context.getString(R.string.wifi_unmetered_label);
}
+ /**
+ * Returns the Internet icon resource for a given RSSI level.
+ *
+ * @param level The number of bars to show (0-4)
+ * @param noInternet True if a connected Wi-Fi network cannot access the Internet
+ * @throws IllegalArgumentException if an invalid RSSI level is given.
+ */
+ public static int getInternetIconResource(int level, boolean noInternet) {
+ if (level < 0 || level >= WIFI_PIE.length) {
+ throw new IllegalArgumentException("No Wifi icon found for level: " + level);
+ }
+ return noInternet ? NO_INTERNET_WIFI_PIE[level] : WIFI_PIE[level];
+ }
+
public static boolean isMeteredOverridden(WifiConfiguration config) {
return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE;
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
index 53a382a..b0c5314 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
@@ -89,4 +89,24 @@
assertThat(mSpinnerPreference.getSelectedItem())
.isEqualTo(mSpinner.getAdapter().getItem(1));
}
+
+ @Test
+ public void onBindViewHolder_setClickableTrue_isClickableTrue() {
+ mSpinnerPreference.setClickable(true);
+
+ mSpinnerPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mSpinner.isClickable()).isTrue();
+ assertThat(mSpinner.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void onBindViewHolder_setClickableFalse_isClickableFalse() {
+ mSpinnerPreference.setClickable(false);
+
+ mSpinnerPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mSpinner.isClickable()).isFalse();
+ assertThat(mSpinner.isEnabled()).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
new file mode 100644
index 0000000..4a14403
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.airbnb.lottie.LottieAnimationView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+public class IllustrationPreferenceTest {
+
+ @Mock
+ LottieAnimationView mAnimationView;
+
+ private IllustrationPreference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ final Context context = RuntimeEnvironment.application;
+ final AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
+ mPreference = new IllustrationPreference(context, attributeSet);
+ ReflectionHelpers.setField(mPreference, "mIllustrationView", mAnimationView);
+ }
+
+ @Test
+ public void isAnimating_lottieAnimationViewIsNotAnimating_shouldReturnFalse() {
+ when(mAnimationView.isAnimating()).thenReturn(false);
+
+ assertThat(mPreference.isAnimating()).isFalse();
+ }
+
+ @Test
+ public void isAnimating_lottieAnimationViewIsAnimating_shouldReturnTrue() {
+ when(mAnimationView.isAnimating()).thenReturn(true);
+
+ assertThat(mPreference.isAnimating()).isTrue();
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 01d66e6..3219b2b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4944,6 +4944,15 @@
String.valueOf(defAccessibilityButtonMode), /* tag= */
null, /* makeDefault= */ true,
SettingsState.SYSTEM_PACKAGE_NAME);
+
+ if (hasValueInA11yButtonTargets(secureSettings)) {
+ secureSettings.insertSettingLocked(
+ Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
+ /* enabled */ "1",
+ /* tag= */ null,
+ /* makeDefault= */ false,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
}
}
@@ -5170,13 +5179,21 @@
}
private boolean isAccessibilityButtonInNavigationBarOn(SettingsState secureSettings) {
- final boolean hasValueInA11yBtnTargets = !TextUtils.isEmpty(
- secureSettings.getSettingLocked(
- Secure.ACCESSIBILITY_BUTTON_TARGETS).getValue());
+ return hasValueInA11yButtonTargets(secureSettings) && !isGestureNavigateEnabled();
+ }
+
+ private boolean isGestureNavigateEnabled() {
final int navigationMode = getContext().getResources().getInteger(
com.android.internal.R.integer.config_navBarInteractionMode);
+ return navigationMode == NAV_BAR_MODE_GESTURAL;
+ }
- return hasValueInA11yBtnTargets && (navigationMode != NAV_BAR_MODE_GESTURAL);
+ private boolean hasValueInA11yButtonTargets(SettingsState secureSettings) {
+ final Setting a11yButtonTargetsSettings =
+ secureSettings.getSettingLocked(Secure.ACCESSIBILITY_BUTTON_TARGETS);
+
+ return !a11yButtonTargetsSettings.isNull()
+ && !TextUtils.isEmpty(a11yButtonTargetsSettings.getValue());
}
}
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 22e38f4..3a82434 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -752,6 +752,7 @@
Settings.Secure.SUPPRESS_DOZE,
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
Settings.Secure.UI_TRANSLATION_ENABLED);
@Test
diff --git a/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.xml b/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.xml
new file mode 100644
index 0000000..46e7dcc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorAccentPrimary" />
+ <corners android:radius="@dimen/accessibility_floating_tooltip_text_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/ic_conversation_icon.xml b/packages/SystemUI/res/drawable/ic_conversation_icon.xml
new file mode 100644
index 0000000..0e3533b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_conversation_icon.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:pathData="M24,24m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
+ android:fillColor="#81C995"/>
+ <path
+ android:pathData="M27,34C23.134,34 20,30.866 20,27C20,23.134 23.134,20 27,20C30.866,20 34,23.134 34,27C34,28.4872 33.5362,29.8662 32.7453,31"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#3C4043"/>
+ <path
+ android:pathData="M35,33l-8,0l-0,2l8,0z"
+ android:fillColor="#3C4043"/>
+ <path
+ android:pathData="M21,21m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"
+ android:fillColor="#81C995"/>
+ <path
+ android:pathData="M16,25h5v2h-5z"
+ android:fillColor="#81C995"/>
+ <path
+ android:pathData="M21,28C24.866,28 28,24.866 28,21C28,17.134 24.866,14 21,14C17.134,14 14,17.134 14,21C14,22.4872 14.4638,23.8662 15.2547,25"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#ffffff"/>
+ <path
+ android:pathData="M13,27h8v2h-8z"
+ android:fillColor="#ffffff"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_corp_badge_off.xml b/packages/SystemUI/res/drawable/ic_corp_badge_off.xml
new file mode 100644
index 0000000..a441fb2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_corp_badge_off.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20.0"
+ android:viewportHeight="20.0">
+ <path
+ android:pathData="M10,10m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
+ android:fillColor="#607D8B"/>
+ <path
+ android:pathData="M16.42,15.68l-0.85,-0.85L7.21,6.47L4.9,4.16L4.16,4.9l1.57,1.57H5.36c-0.65,0 -1.16,0.52 -1.16,1.17L4.2,14.05c0,0.65 0.52,1.17 1.17,1.17h9.12l1.2,1.2L16.42,15.68zM15.83,7.64c0.03,-0.65 -0.49,-1.17 -1.14,-1.14h-2.33V5.3c0,-0.65 -0.52,-1.17 -1.17,-1.14H8.86C8.22,4.14 7.7,4.66 7.7,5.3v0.19l8.14,8.17V7.64zM11.2,6.5H8.83V5.3h2.36V6.5z"
+ android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_empty_background.xml b/packages/SystemUI/res/drawable/people_tile_empty_background.xml
new file mode 100644
index 0000000..2dac740
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_empty_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:radius="@dimen/people_space_widget_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_status_scrim.xml b/packages/SystemUI/res/drawable/people_tile_status_scrim.xml
new file mode 100644
index 0000000..cf16f1c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_status_scrim.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <gradient
+ android:type="linear"
+ android:angle="90"
+ android:endColor="@android:color/transparent"
+ android:startColor="?androidprv:attr/colorSurfaceHeader" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml b/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml
new file mode 100644
index 0000000..e0c111d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <solid android:color="?androidprv:attr/colorSurfaceVariant" />
+ <corners android:radius="@dimen/people_space_widget_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/wallet_action_button_bg.xml b/packages/SystemUI/res/drawable/wallet_action_button_bg.xml
index bf7625f..925ab67 100644
--- a/packages/SystemUI/res/drawable/wallet_action_button_bg.xml
+++ b/packages/SystemUI/res/drawable/wallet_action_button_bg.xml
@@ -14,13 +14,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item>
<shape android:shape="rectangle">
<stroke
android:width="1dp"
- android:color="@color/GM2_blue_600"/>
- <solid android:color="@color/GM2_blue_600"/>
+ android:color="?androidprv:attr/colorAccentPrimary"/>
+ <solid android:color="?androidprv:attr/colorAccentPrimary"/>
<corners android:radius="24dp"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml b/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml
index f52889a..b203228 100644
--- a/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml
+++ b/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?android:attr/colorControlHighlight">
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="#DADCE0" />
@@ -26,7 +26,7 @@
<shape>
<stroke
android:width="1dp"
- android:color="@color/GM2_grey_900" />
+ android:color="?androidprv:attr/colorAccentPrimary" />
<corners android:radius="@dimen/wallet_empty_state_corner_radius" />
</shape>
</item>
diff --git a/packages/SystemUI/res/layout/accessibility_floating_menu_tooltip.xml b/packages/SystemUI/res/layout/accessibility_floating_menu_tooltip.xml
new file mode 100644
index 0000000..5e7b7e1
--- /dev/null
+++ b/packages/SystemUI/res/layout/accessibility_floating_menu_tooltip.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tooltip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <View
+ android:id="@+id/arrow_left"
+ android:layout_width="@dimen/accessibility_floating_tooltip_arrow_width"
+ android:layout_height="@dimen/accessibility_floating_tooltip_arrow_height"
+ android:layout_marginRight="@dimen/accessibility_floating_tooltip_arrow_margin"
+ android:visibility="gone"
+ android:layout_gravity="center_vertical"/>
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:padding="@dimen/accessibility_floating_tooltip_padding"
+ android:background="@drawable/accessibility_floating_tooltip_background"
+ android:textColor="@android:color/black"
+ android:textColorLink="@android:color/black"
+ android:text="@string/accessibility_floating_button_migration_tooltip"
+ android:textSize="@dimen/accessibility_floating_tooltip_font_size"/>
+
+ <View
+ android:id="@+id/arrow_right"
+ android:layout_width="@dimen/accessibility_floating_tooltip_arrow_width"
+ android:layout_height="@dimen/accessibility_floating_tooltip_arrow_height"
+ android:layout_marginLeft="@dimen/accessibility_floating_tooltip_arrow_margin"
+ android:visibility="gone"
+ android:layout_gravity="center_vertical"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index ceba4e3..b99c86f 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -126,8 +126,8 @@
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/save"
app:layout_constraintStart_toStartOf="parent"
- android:scaleType="matrix"
- android:visibility="gone"
+ android:scaleType="centerCrop"
+ android:visibility="invisible"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index 8c54e2c..9e67258 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -75,7 +75,7 @@
android:id="@+id/media_logo1"
android:layout_width="@dimen/qs_media_icon_size"
android:layout_height="@dimen/qs_media_icon_size"
- style="@style/MediaPlayer.AppIcon" />
+ style="@style/MediaPlayer.AppIcon.Recommendation" />
<ImageView
android:id="@+id/media_cover2"
@@ -91,7 +91,7 @@
android:id="@+id/media_logo2"
android:layout_width="@dimen/qs_media_icon_size"
android:layout_height="@dimen/qs_media_icon_size"
- style="@style/MediaPlayer.AppIcon" />
+ style="@style/MediaPlayer.AppIcon.Recommendation" />
<ImageView
android:id="@+id/media_cover3"
@@ -107,7 +107,7 @@
android:id="@+id/media_logo3"
android:layout_width="@dimen/qs_media_icon_size"
android:layout_height="@dimen/qs_media_icon_size"
- style="@style/MediaPlayer.AppIcon" />
+ style="@style/MediaPlayer.AppIcon.Recommendation" />
<ImageView
android:id="@+id/media_cover4"
@@ -123,7 +123,7 @@
android:id="@+id/media_logo4"
android:layout_width="@dimen/qs_media_icon_size"
android:layout_height="@dimen/qs_media_icon_size"
- style="@style/MediaPlayer.AppIcon" />
+ style="@style/MediaPlayer.AppIcon.Recommendation" />
<ImageView
android:id="@+id/media_cover5"
@@ -139,7 +139,7 @@
android:id="@+id/media_logo5"
android:layout_width="@dimen/qs_media_icon_size"
android:layout_height="@dimen/qs_media_icon_size"
- style="@style/MediaPlayer.AppIcon" />
+ style="@style/MediaPlayer.AppIcon.Recommendation" />
<ImageView
android:id="@+id/media_cover6"
@@ -155,7 +155,7 @@
android:id="@+id/media_logo6"
android:layout_width="@dimen/qs_media_icon_size"
android:layout_height="@dimen/qs_media_icon_size"
- style="@style/MediaPlayer.AppIcon" />
+ style="@style/MediaPlayer.AppIcon.Recommendation" />
<!-- Long press menu -->
<TextView
diff --git a/packages/SystemUI/res/layout/people_status_scrim_layout.xml b/packages/SystemUI/res/layout/people_status_scrim_layout.xml
new file mode 100644
index 0000000..9808f74
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_status_scrim_layout.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+~ Copyright (C) 2021 The Android Open Source Project
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scrim_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/status_icon"
+ android:scaleType="centerCrop"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:weightSum="1"
+ android:orientation="vertical">
+ <ImageView
+ android:layout_weight=".2"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ <ImageView
+ android:background="@drawable/people_tile_status_scrim"
+ android:layout_weight=".8"
+ android:scaleType="centerCrop"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:weightSum="1"
+ android:orientation="vertical">
+ <ImageView
+ android:layout_weight=".66"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ <ImageView
+ android:background="@drawable/people_tile_status_scrim"
+ android:layout_weight=".33"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ </LinearLayout>
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/people_tile_empty_layout.xml b/packages/SystemUI/res/layout/people_tile_empty_layout.xml
index 7d3b919..8e9ebc6 100644
--- a/packages/SystemUI/res/layout/people_tile_empty_layout.xml
+++ b/packages/SystemUI/res/layout/people_tile_empty_layout.xml
@@ -14,16 +14,17 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
- android:background="@drawable/people_space_tile_view_card"
+ android:background="@drawable/people_tile_empty_background"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
- android:id="@+id/item"
+ android:id="@+id/icon"
+ android:src="@drawable/ic_conversation_icon"
android:gravity="center"
android:layout_gravity="center"
- android:padding="8dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
index 6f8de3b..b77670e 100644
--- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
@@ -1,145 +1,159 @@
<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@android:style/Theme.DeviceDefault.DayNight"
- android:id="@+id/item"
+~ Copyright (C) 2021 The Android Open Source Project
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/people_space_tile_view_card"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:id="@+id/item"
+ android:clipToOutline="true"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:layout_gravity="center"
- android:padding="16dp"
- android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
- <RelativeLayout
+ <include layout="@layout/people_status_scrim_layout" />
+
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="start|top">
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:padding="16dp"
+ android:orientation="vertical">
- <LinearLayout
- android:layout_width="wrap_content"
+ <RelativeLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:gravity="start|top"
- android:orientation="horizontal">
+ android:gravity="start|top">
- <ImageView
- android:id="@+id/person_icon"
- android:layout_marginStart="-2dp"
- android:layout_marginTop="-2dp"
+ <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1" />
+ android:layout_alignParentStart="true"
+ android:gravity="start|top"
+ android:orientation="horizontal">
- <ImageView
- android:id="@+id/availability"
- android:layout_marginStart="-2dp"
- android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="@drawable/circle_green_10dp" />
- </LinearLayout>
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_marginStart="-2dp"
+ android:layout_marginTop="-2dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
- <TextView
- android:id="@+id/messages_count"
- android:layout_alignParentEnd="true"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:background="@drawable/people_space_messages_count_background"
- android:textSize="14sp"
- android:maxLines="1"
- android:ellipsize="end"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- />
- </RelativeLayout>
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <include layout="@layout/people_tile_punctuation_background_large" />
- <include layout="@layout/people_tile_emoji_background_large" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView
- android:layout_gravity="center"
- android:id="@+id/name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="12dp"
- android:gravity="start"
- android:singleLine="true"
- android:ellipsize="end"
- android:text="@string/empty_user_name"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="4dp"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/predefined_icon"
- android:tint="?android:attr/colorAccent"
- android:gravity="start|center_vertical"
- android:paddingEnd="6dp"
- android:layout_width="24dp"
- android:layout_height="18dp" />
+ <ImageView
+ android:id="@+id/availability"
+ android:layout_marginStart="-2dp"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="@drawable/circle_green_10dp" />
+ </LinearLayout>
<TextView
- android:layout_gravity="center"
- android:id="@+id/subtext"
- android:gravity="center_vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:singleLine="true"
- android:text="@string/empty_user_name"
+ android:id="@+id/messages_count"
+ android:layout_alignParentEnd="true"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="12sp" />
- </LinearLayout>
+ android:textColor="?android:attr/textColorPrimary"
+ android:background="@drawable/people_space_messages_count_background"
+ android:textSize="14sp"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+ </RelativeLayout>
- <ImageView
- android:id="@+id/image"
+ <RelativeLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/people_space_content_background"
- android:gravity="center"
- android:scaleType="centerCrop" />
+ android:layout_height="match_parent">
- <TextView
- android:layout_gravity="center"
- android:id="@+id/text_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="2"
- android:singleLine="false"
- android:text="@string/empty_status"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="12sp" />
- </LinearLayout>
- </RelativeLayout>
-</LinearLayout>
+ <include layout="@layout/people_tile_punctuation_background_large" />
+
+ <include layout="@layout/people_tile_emoji_background_large" />
+
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="12dp"
+ android:gravity="start"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp" />
+
+ <LinearLayout
+ android:id="@+id/status_icon_and_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="4dp"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/predefined_icon"
+ android:tint="?android:attr/colorAccent"
+ android:gravity="start|center_vertical"
+ android:paddingEnd="6dp"
+ android:layout_width="24dp"
+ android:layout_height="18dp" />
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/subtext"
+ android:gravity="center_vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="12sp" />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/people_space_content_background"
+ android:gravity="center"
+ android:scaleType="centerCrop" />
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/text_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:singleLine="false"
+ android:text="@string/empty_status"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/content_text_size_for_large" />
+ </LinearLayout>
+ </RelativeLayout>
+ </LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index a8c15ab..8df0bf8 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -17,18 +17,20 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:id="@+id/item"
+ android:background="@drawable/people_space_tile_view_card"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
- android:background="@drawable/people_space_tile_view_card"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/people_tile_punctuation_background_medium" />
- <include layout="@layout/people_tile_emoji_background_medium" />
+ <include layout="@layout/people_tile_punctuation_background_medium" />
+ <include layout="@layout/people_status_scrim_layout" />
<LinearLayout
- android:id="@+id/item"
+ android:id="@+id/content"
android:orientation="vertical"
android:layout_gravity="center"
android:padding="8dp"
@@ -89,7 +91,7 @@
android:text="@string/empty_status"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="12sp"
+ android:textSize="@dimen/content_text_size_for_medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
diff --git a/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml b/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
new file mode 100644
index 0000000..b151c60
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:background="@drawable/people_tile_suppressed_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml b/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
new file mode 100644
index 0000000..25ab5a6
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
@@ -0,0 +1,39 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/item"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:background="@drawable/people_tile_suppressed_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
+
+ <ImageView android:id="@+id/work_widget_badge_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:layout_marginBottom="12dp"
+ android:layout_marginRight="12dp"
+ android:src="@drawable/ic_corp_badge_off"
+ android:clickable="false" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index f0229a6..a62310b 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -28,7 +28,7 @@
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="match_parent"
android:minWidth="48dp"
android:minHeight="48dp"
@@ -49,17 +49,35 @@
android:layout_gravity="end|center_vertical"
android:focusable="false"/>
- <com.android.systemui.statusbar.phone.StatusIconContainer
- android:id="@+id/statusIcons"
- android:layout_width="wrap_content"
+ <View
+ android:id="@+id/separator"
+ android:layout_width="0dp"
android:layout_height="match_parent"
- android:paddingEnd="@dimen/signal_cluster_battery_padding" />
+ android:layout_gravity="center"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:visibility="gone"
+ />
- <com.android.systemui.BatteryMeterView
- android:id="@+id/batteryRemainingIcon"
+ <LinearLayout
+ android:id="@+id/rightLayout"
+ android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_width="wrap_content"
- systemui:textAppearance="@style/TextAppearance.QS.Status"
- android:paddingEnd="2dp" />
+ android:layout_weight="1"
+ >
+ <com.android.systemui.statusbar.phone.StatusIconContainer
+ android:id="@+id/statusIcons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingEnd="@dimen/signal_cluster_battery_padding" />
+ <com.android.systemui.BatteryMeterView
+ android:id="@+id/batteryRemainingIcon"
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ systemui:textAppearance="@style/TextAppearance.QS.Status"
+ android:paddingEnd="2dp" />
+
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/smart_action_button.xml b/packages/SystemUI/res/layout/smart_action_button.xml
index 2716034..488be3a 100644
--- a/packages/SystemUI/res/layout/smart_action_button.xml
+++ b/packages/SystemUI/res/layout/smart_action_button.xml
@@ -29,6 +29,8 @@
android:textSize="@dimen/smart_reply_button_font_size"
android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
android:textColor="@color/smart_reply_button_text"
+ android:paddingLeft="@dimen/smart_reply_button_action_padding_left"
+ android:paddingRight="@dimen/smart_reply_button_padding_horizontal"
android:drawablePadding="@dimen/smart_action_button_icon_padding"
android:textStyle="normal"
android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml
index 9faed18..ddf16e0 100644
--- a/packages/SystemUI/res/layout/smart_reply_button.xml
+++ b/packages/SystemUI/res/layout/smart_reply_button.xml
@@ -31,5 +31,7 @@
android:textSize="@dimen/smart_reply_button_font_size"
android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
android:textColor="@color/smart_reply_button_text"
+ android:paddingLeft="@dimen/smart_reply_button_padding_horizontal"
+ android:paddingRight="@dimen/smart_reply_button_padding_horizontal"
android:textStyle="normal"
android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/layout/smart_reply_view.xml b/packages/SystemUI/res/layout/smart_reply_view.xml
index 9fffc72..9d4d1db 100644
--- a/packages/SystemUI/res/layout/smart_reply_view.xml
+++ b/packages/SystemUI/res/layout/smart_reply_view.xml
@@ -24,8 +24,6 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
systemui:spacing="@dimen/smart_reply_button_spacing"
- systemui:singleLineButtonPaddingHorizontal="@dimen/smart_reply_button_padding_horizontal_single_line"
- systemui:doubleLineButtonPaddingHorizontal="@dimen/smart_reply_button_padding_horizontal_double_line"
systemui:buttonStrokeWidth="@dimen/smart_reply_button_stroke_width">
<!-- smart_reply_button(s) will be added here. -->
</com.android.systemui.statusbar.policy.SmartReplyView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index c3c291b..412276d 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -19,8 +19,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
android:visibility="gone">
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/content"
diff --git a/packages/SystemUI/res/layout/wallet_empty_state.xml b/packages/SystemUI/res/layout/wallet_empty_state.xml
index 3ca0f73..cc2e781 100644
--- a/packages/SystemUI/res/layout/wallet_empty_state.xml
+++ b/packages/SystemUI/res/layout/wallet_empty_state.xml
@@ -16,7 +16,8 @@
** limitations under the License.
*/
-->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<LinearLayout
android:id="@+id/wallet_empty_state"
android:layout_width="match_parent"
@@ -35,6 +36,7 @@
android:layout_height="28dp"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
+ android:tint="?androidprv:attr/colorAccentPrimary"
android:contentDescription="@null"
android:scaleType="fitCenter"/>
<TextView
diff --git a/packages/SystemUI/res/layout/wallet_fullscreen.xml b/packages/SystemUI/res/layout/wallet_fullscreen.xml
index bbb180f..d365aa3 100644
--- a/packages/SystemUI/res/layout/wallet_fullscreen.xml
+++ b/packages/SystemUI/res/layout/wallet_fullscreen.xml
@@ -16,6 +16,8 @@
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
@@ -34,8 +36,8 @@
android:orientation="vertical">
<ImageView
android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/wallet_screen_header_view_size"
+ android:layout_height="@dimen/wallet_screen_header_view_size"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="10dp"
android:scaleType="center"
@@ -65,7 +67,7 @@
android:paddingVertical="@dimen/wallet_button_vertical_padding"
android:paddingHorizontal="@dimen/wallet_button_horizontal_padding"
android:background="@drawable/wallet_action_button_bg"
- android:textColor="@color/wallet_white"
+ android:textColor="?androidprv:attr/textColorPrimaryInverse"
android:textAlignment="center"
android:visibility="gone"/>
@@ -83,7 +85,7 @@
android:paddingHorizontal="@dimen/wallet_button_horizontal_padding"
android:background="@drawable/wallet_app_button_bg"
android:text="@string/wallet_app_button_label"
- android:textColor="@color/GM2_blue_600"
+ android:textColor="?androidprv:attr/colorAccentPrimary"
android:textAlignment="center"
android:layout_marginVertical="24dp"/>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index d3ab5b3..e000ff4 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiele warmkol afgeskakel."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiele warmkol aangeskakel."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Uitsaai van skerm gestaak."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Werkmodus is onderbreek."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Werkmodus is aan."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Werkmodus is verander na onderbreek."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Werkmodus is aangeskakel."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Databespaarder is afgeskakel."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Databespaarder is aangeskakel."</string>
@@ -412,8 +410,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g>-limiet"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> waarskuwing"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Werkprofiel"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Onderbreek"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Aandbeligting"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aan by sonsondergang"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Tot sonsopkoms"</string>
@@ -473,14 +470,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Volkome\nstilte"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Net\nprioriteit"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Net\nwekkers"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans draadloos • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans vinnig • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans stadig • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Wissel gebruiker, huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Beursie"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Wys alles"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Ontsluit om te betaal"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nie opgestel nie"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontsluit om te gebruik"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kon nie jou kaarte kry nie; probeer later weer"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Sluitskerminstellings"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
<string name="add_tile" msgid="6239678623873086686">"Voeg teël by"</string>
@@ -871,8 +862,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Aan"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Af"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Onbeskikbaar"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Gedeaktiveer"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigasiebalk"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Uitleg"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra linksknoppie-tipe"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 4dee085..e69ab3f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ሙሉ ለሙሉ\nጸጥታ"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ቅድሚያ ተሰጪ\nብቻ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"ማንቂያዎች\nብቻ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በገመድ-አልባ ኃይል በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ኃይል በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ተጠቃሚ ይለውጡ፣ የአሁን ተጠቃሚ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"የአሁን ተጠቃሚ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index dd342ff..7c8d105 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -475,14 +475,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Potpuna\ntišina"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Samo\npriorit. prekidi"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Samo\nalarmi"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bežično se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Puni se • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Brzo se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Promenite korisnika, aktuelni korisnik je <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuelni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index adbef20..73efaed 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"একদম\nনিরব"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"শুধুমাত্র\nঅগ্রাধিকার"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"শুধুমাত্র\nঅ্যালার্মগুলি"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ওয়্যারলেস পদ্ধতিতে চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চার্জিং • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • দ্রুত চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ধীরে চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ব্যবহারকারী পাল্টান, বর্তমান ব্যবহারকারী <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"<xliff:g id="CURRENT_USER_NAME">%s</xliff:g> হল বর্তমান ব্যবহারকারী"</string>
@@ -750,14 +746,10 @@
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>স্ট্যাটাস:</b> লেভেল কমিয়ে সাইলেন্ করা হয়েছে"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>স্ট্যাটাস:</b> র্যাঙ্ক বেড়ে গেছে"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>স্ট্যাটাস:</b> র্যাঙ্ক কমে গেছে"</string>
- <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
- <skip />
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়, বাবল হিসেবেও এটি দেখা যায়"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয় এবং এর ফলে \'বিরক্ত করবে না\' মোডে কাজ করতে অসুবিধা হয়"</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়, বাবল হিসেবেও এটি দেখা যায় এবং এর ফলে \'বিরক্ত করবে না\' মোডে কাজ করতে অসুবিধা হয়"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"সেটিংস"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"অগ্রাধিকার"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এ কথোপকথন ফিচার কাজ করে না"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index a54dffe..5c9ec89 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -475,14 +475,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Potpuna\ntišina"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Samo\nprioritetni prekidi"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Samo\nalarmi"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bežično punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Brzo punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Zamijeni korisnika. Trenutni korisnik je <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Trenutni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 803bbd1..fc3b2f5 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silenci\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Només\ninterr. prior."</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Només\nalarmes"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant sense fil • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • S\'està carregant • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant ràpidament • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant lentament • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Canvia d\'usuari. Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 8ed760a..4560cad 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -477,14 +477,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Úplné\nticho"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Pouze\nprioritní"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Pouze\nbudíky"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bezdrátové nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Rychlé nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Pomalé nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Přepnout uživatele, aktuální uživatel: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuální uživatel <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index c76ebc3..a30e0636 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilhotspot er slået fra."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilhotspot er slået til."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Casting af din skærm er stoppet."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Arbejdstilstand er på pause."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Arbejdstilstand er slået til."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Arbejdstilstand er sat på pause."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Arbejdstilstand er slået til."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Datasparefunktionen er slået fra."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Datasparefunktionen er aktiveret."</string>
@@ -412,8 +410,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Grænse: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advarsel ved <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Arbejdsprofil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Sat på pause"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nattelys"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Tænd ved solnedgang"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Indtil solopgang"</string>
@@ -473,14 +470,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Total\nstilhed"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Kun\nprioritet"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Kun\nalarmer"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Trådløs opladning • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader hurtigt • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader langsomt • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Skift bruger. Nuværende bruger er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Nuværende bruger: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Vis alle"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Lås op for at betale"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Ikke konfigureret"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås op for at bruge"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Dine kort kunne ikke hentes. Prøv igen senere."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lås skærmindstillinger"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbejdsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flytilstand"</string>
<string name="add_tile" msgid="6239678623873086686">"Tilføj et felt"</string>
@@ -871,8 +862,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Til"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Fra"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Ikke tilgængelig"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Deaktiveret"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigationslinje"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra venstre knaptype"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 3da19fc..fd8af9d 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Πλήρης\nσίγαση"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Μόνο\nπροτεραιότητας"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Μόνο\nειδοποιήσεις"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ασύρματη φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Γρήγορη φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Αργή φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Εναλλαγή χρήστη, τρέχων χρήστης <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Τρέχων χρήστης <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 15347dd..58a31df 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Zona móvil desactivada"</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Zona móvil activada"</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmisión de pantalla detenida"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabajo pausado."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabajo activado"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Se pausó el modo de trabajo."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Se activó el modo de trabajo."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Se desactivó el Ahorro de datos."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Se activó el Ahorro de datos."</string>
@@ -412,8 +410,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabajo"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausado"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luz nocturna"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Al atardecer"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hasta el amanecer"</string>
@@ -473,14 +470,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silencio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Solo\nprioridad"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Solo\nalarmas"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando de manera inalámbrica • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando rápido • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lento • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambiar de usuario (usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"El usuario actual es <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar todo"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloquear para pagar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Sin configurar"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocurrió un problema al obtener las tarjetas; vuelve a intentarlo más tarde"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración de pantalla de bloqueo"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avión"</string>
<string name="add_tile" msgid="6239678623873086686">"Agregar mosaico"</string>
@@ -871,8 +862,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desactivado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"No disponible"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Inhabilitada"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegación"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Diseño"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botón izquierdo adicional"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 57ce1c5..a863111 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silencio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Solo\ncon prioridad"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Solo\nalarmas"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga sin cables • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambiar de usuario (usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 85679b9..932ce3e 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Isiltasun\nosoa"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Lehentasunezkoak\nsoilik"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Alarmak\nsoilik"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hari gabe kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bizkor kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mantso kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Aldatu erabiltzailea. <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> da saioa hasita daukana."</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Erabiltzailea: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 052a87a..89b4fbd 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"نقطه اتصال دستگاه همراه خاموش شد."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"نقطه اتصال دستگاه همراه روشن شد."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"فرستادن صفحه نمایش متوقف شد."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"حالت کار موقتاً متوقف شده است."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"حالت کار روشن."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"حالت کار موقتاً متوقف شد."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"حالت کار روشن شد."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"صرفهجویی داده خاموش شد."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"صرفهجویی داده روشن شد."</string>
@@ -412,8 +410,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> محدودیت"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"هشدار <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"نمایه کاری"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"موقتاً متوقف"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"نور شب"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"غروب روشن میشود"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"تا طلوع"</string>
@@ -473,14 +470,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"سکوت\nکامل"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"فقط\nاولویتدار"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"فقط\nهشدارها"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن بیسیم • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن سریع • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن آهسته • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"تعویض کاربر، کاربر کنونی <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"کاربر کنونی <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
<string name="wallet_title" msgid="5369767670735827105">"کیفپول"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"نمایش همه"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"باز کردن قفل برای پرداخت"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"تنظیمنشده"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"برای استفاده، قفل را باز کنید"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"هنگام دریافت کارتها مشکلی پیش آمد، لطفاً بعداً دوباره امتحان کنید"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"تنظیمات صفحه قفل"</string>
<string name="status_bar_work" msgid="5238641949837091056">"نمایه کاری"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"حالت هواپیما"</string>
<string name="add_tile" msgid="6239678623873086686">"افزودن کاشی"</string>
@@ -871,8 +862,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"روشن"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"خاموش"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"در دسترس نیست"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"غیرفعال"</string>
<string name="nav_bar" msgid="4642708685386136807">"نوار پیمایش"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"طرحبندی"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"نوع دکمه منتهیالیه چپ"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index efe1833..a9acd50 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Aucune\ninterruption"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Priorité\nuniquement"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Alarmes\nuniquement"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge sans fil • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge rapide • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge lente • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Changer d\'utilisateur (utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d5da5d5..083435b 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silencio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Só\nprioridade"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Só\nalarmas"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando sen fíos • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando rapidamente • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lentamente • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambiar usuario, usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 7e92ccf..99d3af6 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -750,14 +750,10 @@
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>સ્ટેટસ:</b> સાઇલન્ટ પર અવનત કરવામાં આવ્યું"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>સ્ટેટસ:</b> ઉપલી રેંક આપવામાં આવી"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>સ્ટેટસ:</b> નીચલી રેંક આપવામાં આવી"</string>
- <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
- <skip />
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, બબલ તરીકે દેખાય છે"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, ખલેલ પાડશો નહીં મોડમાં વિક્ષેપ ઊભો કરે છે"</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, બબલ તરીકે દેખાય છે, ખલેલ પાડશો નહીં મોડમાં વિક્ષેપ ઊભો કરે છે"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"સેટિંગ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"પ્રાધાન્યતા"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> વાતચીતની સુવિધાઓને સપોર્ટ આપતી નથી"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index cfe2079..b526ff6 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"पूरी तरह\nशांत"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"केवल\nप्राथमिकता"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"केवल\nअलार्म"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • वायरलेस तरीके से चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • तेज़ चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • धीरे चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"उपयोगकर्ता बदलें, मौजूदा उपयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"मौजूदा उपयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 9246023..fedeb77 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -475,14 +475,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Potpuna\ntišina"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Samo\nprioritetno"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Samo\nalarmi"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • bežično punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • brzo punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • sporo punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Promjena korisnika, trenutačni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Trenutačan korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 3cfbaba..d3bd516 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Teljes\nnémítás"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Csak\nprioritás"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Csak\nriasztások"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Vezeték nélküli töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Gyors töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lassú töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Felhasználóváltás (a jelenlegi felhasználó: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Jelenlegi felhasználó (<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 4295b96..22feecfc 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Algjör\nþögn"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Aðeins\nforgangur"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Aðeins\nvekjarar"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Þráðlaus hleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Í hleðslu • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hraðhleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hæg hleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Skipta um notanda; núverandi notandi er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Núverandi notandi er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 78b85c9..6cc699b 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silenzio\ntotale"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Solo con\npriorità"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Solo\nsveglie"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica wireless • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ricarica veloce • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ricarica lenta • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambia utente, utente corrente <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utente corrente <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 39abcde..0e331de 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"サイレント\n"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"重要な\n通知のみ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"アラーム\nのみ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ワイヤレス充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 急速充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 低速充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ユーザーを切り替える、現在のユーザーは<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"現在のユーザー: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 78f228a..fddf305 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ស្ងៀមស្ងាត់\nទាំងស្រុង"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"អាទិភាព\nប៉ុណ្ណោះ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"សំឡេងរោទ៍\nប៉ុណ្ណោះ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្មឥតខ្សែ • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្ម • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្មយឺត • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរអ្នកប្រើ"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ប្ដូរអ្នកប្រើ អ្នកប្រើបច្ចុប្បន្ន <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"អ្នកប្រើបច្ចុប្បន្ន <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 3cb20af..8d542ddf 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"ಮೊಬೈಲ್ ಹಾಟ್ಸ್ಪಾಟ್ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"ಮೊಬೈಲ್ ಹಾಟ್ಸ್ಪಾಟ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"ಸ್ಕ್ರೀನ್ ಪ್ರಸಾರವನ್ನು ನಿಲ್ಲಿಸಲಾಗಿದೆ."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"ಕೆಲಸದ ಮೋಡ್ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"ಕೆಲಸದ ಮೋಡ್ ಆನ್ ಆಗಿದೆ."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"ಕೆಲಸದ ಮೋಡ್ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"ಕೆಲಸದ ಮೋಡ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ಡೇಟಾ ಸೇವರ್ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ಡೇಟಾ ಸೇವರ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
@@ -412,8 +410,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಮಿತಿ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಎಚ್ಚರಿಕೆ"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"ನೈಟ್ ಲೈಟ್"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ಸೂರ್ಯಾಸ್ತದಲ್ಲಿ"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ಸೂರ್ಯೋದಯದವರೆಗೆ"</string>
@@ -676,12 +673,10 @@
<string name="wallet_title" msgid="5369767670735827105">"ವಾಲೆಟ್"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"ಎಲ್ಲವನ್ನೂ ತೋರಿಸಿ"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ಪಾವತಿಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"ಇನ್ನೂ ಸೆಟಪ್ ಮಾಡಿಲ್ಲ"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ಬಳಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ನಿಮ್ಮ ಕಾರ್ಡ್ಗಳನ್ನು ಪಡೆಯುವಾಗ ಸಮಸ್ಯೆ ಉಂಟಾಗಿದೆ, ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ಲಾಕ್ ಸ್ಕ್ರ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ಏರ್ಪ್ಲೇನ್ ಮೋಡ್"</string>
<string name="add_tile" msgid="6239678623873086686">"ಟೈಲ್ ಸೇರಿಸಿ"</string>
@@ -871,8 +866,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ಆನ್"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ಆಫ್"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ಲಭ್ಯವಿಲ್ಲ"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="nav_bar" msgid="4642708685386136807">"ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ಲೇಔಟ್"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ಹೆಚ್ಚುವರಿ ಎಡ ಬಟನ್ ವಿಧ"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 91bdc6e..5d70e13 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ຄວາມງຽບ\nທັງໝົດ"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ບຸລິມະສິດ\nເທົ່ານັ້ນ"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"ໂມງປຸກ\nເທົ່ານັ້ນ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບໄຮ້ສາຍ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບໄວ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບຊ້າ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ປ່ຽນຜູ່ໃຊ້, ຜູ່ໃຊ້ປະຈຸບັນ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"ຜູ້ໃຊ້ປະຈຸບັນ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 407db29..fdb8c10 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -477,14 +477,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Visiška\ntyla"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Tik\nprioritetiniai"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Tik\nsignalai"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Įkraunama be laidų • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sparčiai įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lėtai įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Perjungti naudotoją, dabartinis naudotojas <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Dabartinis naudotojas <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index c4524f3..fdb76bb 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Целосна\nтишина"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Само\nприоритетни"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Само\nаларми"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни безжично • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни брзо • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни бавно • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Промени го корисникот, тековен корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Тековен корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 7001a1a..8d3dffd 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Дуугүй\nболгох"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Зөвхөн\nхамгийн чухлыг"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Зөвхөн\nсэрүүлэг"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Утасгүй цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Хурдтай цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Удаан цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Хэрэглэгчийг сэлгэх, одоогийн хэрэглэгч <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Одоогийн хэрэглэгч <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 837d900..267b74a 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Tempat liputan mudah alih bergerak dimatikan."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Tempat liputan mudah alih bergerak dihidupkan."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Penghantaran skrin dihentikan."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Mod kerja dijeda."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Mod kerja hidup."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Mod kerja dijeda."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Mod kerja dihidupkan."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Penjimat Data dimatikan."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Penjimat Data dihidupkan."</string>
@@ -412,8 +410,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> had"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Amaran <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Profil kerja"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Dijeda"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Cahaya Malam"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Dihidupkan pd senja"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hingga matahari terbit"</string>
@@ -676,12 +673,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Tunjukkan semua"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Buka kunci untuk membayar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Tidak disediakan"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Terdapat masalah sewaktu mendapatkan kad anda. Sila cuba sebentar lagi"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Tetapan skrin kunci"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod pesawat"</string>
<string name="add_tile" msgid="6239678623873086686">"Tambahkan jubin"</string>
@@ -871,8 +866,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Hidup"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Mati"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Tidak tersedia"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Dilumpuhkan"</string>
<string name="nav_bar" msgid="4642708685386136807">"Bar navigasi"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Reka letak"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Jenis butang kiri tambahan"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index fa6e62e..cf82ea4 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Total\nstillhet"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Bare\nPrioritet"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Bare\nalarmer"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader trådløst • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader raskt • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader sakte • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Bytt bruker, gjeldende bruker er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Gjeldende bruker: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 26412ce..be1735c 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiele hotspot staat uit."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiele hotspot staat aan."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Casten van scherm gestopt."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Werkmodus is onderbroken."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Werkmodus aan."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Werkmodus is uitgezet."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Werkmodus staat aan."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Databesparing staat uit."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Databesparing staat aan."</string>
@@ -412,8 +410,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limiet van <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Waarschuwing voor <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Werkprofiel"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Onderbroken"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nachtverlichting"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aan bij zonsondergang"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Tot zonsopgang"</string>
@@ -473,14 +470,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Totale\nstilte"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Alleen\nprioriteit"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Alleen\nalarmen"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Draadloos opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Snel opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Langzaam opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Schakelen tussen gebruikers, huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Alles tonen"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Ontgrendelen om te betalen"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Niet ingesteld"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontgrendelen om te gebruiken"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Er is een probleem opgetreden bij het ophalen van je kaarten. Probeer het later opnieuw."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Instellingen voor vergrendelscherm"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
<string name="add_tile" msgid="6239678623873086686">"Tegel toevoegen"</string>
@@ -871,8 +862,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Aan"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Uit"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Niet beschikbaar"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Uit"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigatiebalk"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Lay-out"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Extra knoptype links"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 268c245..9a2cd80 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ସମ୍ପୂର୍ଣ୍ଣ\nନୀରବ"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"କେବଳ\nପ୍ରାଥମିକତା"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"କେବଳ\nଆଲାର୍ମ"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ୱାୟାରଲେସ୍ ଭାବେ ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍ ବଦଳାନ୍ତୁ"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ୟୁଜର୍ ବଦଳାନ୍ତୁ, ବର୍ତ୍ତମାନର ୟୁଜର୍ ହେଉଛନ୍ତି <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"ବର୍ତ୍ତମାନର ୟୁଜର୍ ହେଉଛନ୍ତି <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -750,14 +746,10 @@
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ସ୍ଥିତି:</b> ନୀରବକୁ ଡିମୋଟ୍ କରାଯାଇଛି"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>ସ୍ଥିତି:</b> ରେଙ୍କ ଉପରକୁ କରାଯାଇଛି"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>ସ୍ଥିତି:</b> ରେଙ୍କ ତଳକୁ କରାଯାଇଛି"</string>
- <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
- <skip />
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, ଏକ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, \'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\'କୁ ବାଧା ଦିଏ"</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, ଏକ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, \'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\'କୁ ବାଧା ଦିଏ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ସେଟିଂସ୍"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ପ୍ରାଥମିକତା"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବାର୍ତ୍ତାଳାପ ଫିଚରଗୁଡ଼ିକୁ ସମର୍ଥନ କରେ ନାହିଁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 635cb2f..bc9b2ee 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -750,14 +750,10 @@
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਘਟਾ ਕੇ ਸ਼ਾਂਤ \'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਵਧਾਇਆ ਗਿਆ"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਘਟਾਇਆ ਗਿਆ"</string>
- <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
- <skip />
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ, ਬਬਲ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ, \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵੀ ਵਿਘਨ ਪੈ ਸਕਦਾ ਹੈ"</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ, ਬਬਲ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ, \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵੀ ਵਿਘਨ ਪੈ ਸਕਦਾ ਹੈ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ਸੈਟਿੰਗਾਂ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ਤਰਜੀਹ"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਗੱਲਬਾਤ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 6ee7453..3c6578e 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -477,14 +477,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Całkowita\ncisza"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Tylko\npriorytetowe"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Tylko\nalarmy"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ładowanie bezprzewodowe • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Szybkie ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wolne ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Przełącz użytkownika. Bieżący użytkownik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Bieżący użytkownik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index b15a144..8228c0c 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"O ponto de acesso móvel foi desativado."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"O ponto de acesso móvel foi ativado."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"A transmissão de tela foi interrompida."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabalho pausado."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabalho ativado."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"O modo de trabalho foi pausado."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Modo de trabalho ativado."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Economia de dados desativada."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Economia de dados ativada."</string>
@@ -412,8 +410,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabalho"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausado"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Modo noturno"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ativ. ao pôr do sol"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Até o nascer do sol"</string>
@@ -473,14 +470,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silêncio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Somente\nprioridade"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Somente\nalarmes"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando sem fio • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Alternar usuário. Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Carteira"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloqueie para pagar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Dispositivos não configurados"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
<string name="add_tile" msgid="6239678623873086686">"Adicionar bloco"</string>
@@ -871,8 +862,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponível"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Desativado"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegação"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botão esquerdo extra"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 90b1931..afebc31 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silêncio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Apenas\nprioridade"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Apenas\nalarmes"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar sem fios • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar rapidamente • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar lentamente • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Mudar de utilizador; o utilizador atual é <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utilizador atual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index b15a144..8228c0c 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"O ponto de acesso móvel foi desativado."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"O ponto de acesso móvel foi ativado."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"A transmissão de tela foi interrompida."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabalho pausado."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabalho ativado."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"O modo de trabalho foi pausado."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Modo de trabalho ativado."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Economia de dados desativada."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Economia de dados ativada."</string>
@@ -412,8 +410,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabalho"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausado"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Modo noturno"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ativ. ao pôr do sol"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Até o nascer do sol"</string>
@@ -473,14 +470,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silêncio\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Somente\nprioridade"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Somente\nalarmes"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando sem fio • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Alternar usuário. Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Carteira"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloqueie para pagar"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Dispositivos não configurados"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
<string name="add_tile" msgid="6239678623873086686">"Adicionar bloco"</string>
@@ -871,8 +862,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponível"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Desativado"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegação"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botão esquerdo extra"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 44325f7..407ad62 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilný hotspot je vypnutý."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilný hotspot je zapnutý."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Prenášanie bolo zastavené."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Pracovný režim je pozastavený."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Pracovný režim zapnutý"</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Pracovný režim bol pozastavený."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Pracovný režim je zapnutý."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Šetrič dát bol vypnutý."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Šetrič dát bol zapnutý."</string>
@@ -416,8 +414,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozornenie pri <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Pracovný profil"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pozastavené"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nočný režim"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Zapne sa pri západe slnka"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do východu slnka"</string>
@@ -477,14 +474,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Úplné\nticho"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Iba\nprioritné"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Iba\nbudíky"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa bezdrôtovo • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa rýchlo • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa pomaly • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Prepnúť používateľa (súčasný používateľ: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuálny používateľ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +675,10 @@
<string name="wallet_title" msgid="5369767670735827105">"Peňaženka"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Zobraziť všetko"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Odomknúť a zaplatiť"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nenastavené"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odomknúť a použiť"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pri načítavaní kariet sa vyskytol problém. Skúste to neskôr."</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavenia uzamknutej obrazovky"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovný profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim v lietadle"</string>
<string name="add_tile" msgid="6239678623873086686">"Pridať dlaždicu"</string>
@@ -881,8 +872,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Zapnuté"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Vypnuté"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nedostupné"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Deaktivované"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigačný panel"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Rozloženie"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Dodatočný typ ľavého tlačidla"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 70b53cb..d44bd22 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Heshtje\ne plotë"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Vetëm\nme prioritet"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Vetëm\nalarmet"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet me valë • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet shpejt • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet ngadalë • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Ndërro përdoruesin. Përdoruesi aktual është <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Përdoruesi aktual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -750,14 +746,10 @@
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Statusi:</b> Ulur në nivel si në heshtje"</string>
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>Statusi:</b> Renditur më lart"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>Statusi:</b> Renditur më poshtë"</string>
- <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
- <skip />
- <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
- <skip />
+ <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"Shfaqet në krye të njoftimeve të bisedës dhe si fotografia e profilit në ekranin e kyçjes"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"Shfaqet në krye të njoftimeve të bisedës, shfaqet si fotografia e profilit në ekranin e kyçjes dhe shfaqet si flluskë"</string>
+ <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"Shfaqet në krye të njoftimeve të bisedës, shfaqet si fotografia e profilit në ekranin e kyçjes dhe ndërpret modalitetin \"Mos shqetëso\""</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shfaqet në krye të njoftimeve të bisedës dhe si fotografia e profilit në ekranin e kyçjes, shfaqet si flluskë dhe ndërpret modalitetin \"Mos shqetëso\""</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Cilësimet"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Përparësia"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mbështet veçoritë e bisedës"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index de1c9a4..93fb30f 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -475,14 +475,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Потпуна\nтишина"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Само\nприорит. прекиди"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Само\nаларми"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Бежично се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Пуни се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Брзо се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Споро се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Промените корисника, актуелни корисник је <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Актуелни корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index fcbe07d..3e7e1b1 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -473,14 +473,10 @@
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"Helt\ntyst"</string>
<string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Endast\nprioriterade"</string>
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Endast\nalarm"</string>
- <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
- <skip />
- <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
- <skip />
+ <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas trådlöst • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas snabbt • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas långsamt • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Byt användare. Aktuell användare: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuell användare <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 1c70acc..0ec4a4b 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -289,11 +289,9 @@
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"I-hotspot ivaliwe."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"I-hotspot ivuliwe."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ukusakaza kwesikrini kumisiwe."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Imodi yokusebenza imisiwe."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Imodi yomsebenzi ivuliwe."</string>
- <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
- <skip />
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Imodi yokusebenza imisiwe."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Imodi yomsebenzi ivuliwe."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Iseva yedatha ivaliwe."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Iseva yedatha ivuliwe."</string>
@@ -412,8 +410,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> umkhawulo"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> isexwayiso"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Iphrofayela yomsebenzi"</string>
- <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
- <skip />
+ <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Kumisiwe"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ukukhanya kwasebusuku"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Kuvulwe ekushoneni kwelanga"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Kuze kube sekuphumeni kwelanga"</string>
@@ -676,12 +673,10 @@
<string name="wallet_title" msgid="5369767670735827105">"I-wallet"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Bonisa konke"</string>
<string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Vula ukuze ukhokhele"</string>
- <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
- <skip />
+ <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Akusethiwe"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Vula ukuze usebenzise"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kube khona inkinga yokuthola amakhadi akho, sicela uzame futhi ngemuva kwesikhathi"</string>
- <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
- <skip />
+ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Amasethingi okukhiya isikrini"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Iphrofayela yomsebenzi"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Imodi yendiza"</string>
<string name="add_tile" msgid="6239678623873086686">"Engeza ithayili"</string>
@@ -871,8 +866,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Vuliwe"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Valiwe"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Akutholakali"</string>
- <!-- no translation found for tile_disabled (373212051546573069) -->
- <skip />
+ <string name="tile_disabled" msgid="373212051546573069">"Kukhutshaziwe"</string>
<string name="nav_bar" msgid="4642708685386136807">"Ibha yokuzula"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Isakhiwo"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Uhlobo lwenkinobho engakwesokunxele engeziwe"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index eb72442..067d56f 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -131,8 +131,6 @@
<declare-styleable name="SmartReplyView">
<attr name="spacing" format="dimension" />
- <attr name="singleLineButtonPaddingHorizontal" format="dimension" />
- <attr name="doubleLineButtonPaddingHorizontal" format="dimension" />
<attr name="buttonStrokeWidth" format="dimension" />
</declare-styleable>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 08a2e19..8d2a82c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -286,7 +286,5 @@
<color name="accessibility_floating_menu_stroke_dark">#26FFFFFF</color> <!-- 15% -->
<!-- Wallet screen -->
- <color name="wallet_white">#FFFFFF</color>
<color name="wallet_card_border">#33FFFFFF</color>
- <color name="wallet_primary_text">@color/GM2_grey_900</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2283844..57e6cc3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1147,15 +1147,14 @@
<!-- Smart reply button. Total height 48dp, visible height 32dp. -->
<dimen name="smart_reply_button_spacing">8dp</dimen>
<dimen name="smart_reply_button_padding_vertical">14dp</dimen>
- <!-- Note: The following two paddings need to be different until b/78876518 is fixed. -->
- <dimen name="smart_reply_button_padding_horizontal_single_line">20dp</dimen>
- <dimen name="smart_reply_button_padding_horizontal_double_line">19dp</dimen>
+ <dimen name="smart_reply_button_padding_horizontal">16dp</dimen>
+ <dimen name="smart_reply_button_action_padding_left">8dp</dimen>
<dimen name="smart_reply_button_min_height">48dp</dimen>
<dimen name="smart_reply_button_stroke_width">1dp</dimen>
<dimen name="smart_reply_button_font_size">14sp</dimen>
<dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
<!-- Corner radius = half of min_height to create rounded sides. -->
- <dimen name="smart_reply_button_corner_radius">24dp</dimen>
+ <dimen name="smart_reply_button_corner_radius">8dp</dimen>
<dimen name="smart_action_button_icon_size">18dp</dimen>
<dimen name="smart_action_button_icon_padding">8dp</dimen>
@@ -1443,6 +1442,15 @@
<dimen name="accessibility_floating_menu_large_single_radius">33dp</dimen>
<dimen name="accessibility_floating_menu_large_multiple_radius">35dp</dimen>
+ <dimen name="accessibility_floating_tooltip_arrow_width">8dp</dimen>
+ <dimen name="accessibility_floating_tooltip_arrow_height">16dp</dimen>
+ <dimen name="accessibility_floating_tooltip_arrow_margin">-2dp</dimen>
+ <dimen name="accessibility_floating_tooltip_arrow_corner_radius">2dp</dimen>
+ <dimen name="accessibility_floating_tooltip_text_corner_radius">8dp</dimen>
+ <dimen name="accessibility_floating_tooltip_margin">16dp</dimen>
+ <dimen name="accessibility_floating_tooltip_padding">16dp</dimen>
+ <dimen name="accessibility_floating_tooltip_font_size">14sp</dimen>
+
<dimen name="rounded_slider_height">48dp</dimen>
<!-- rounded_slider_height / 2 -->
<dimen name="rounded_slider_corner_radius">24dp</dimen>
@@ -1484,7 +1492,8 @@
<!-- Wallet activity screen specs -->
<dimen name="wallet_icon_size">36sp</dimen>
- <dimen name="wallet_view_header_icon_size">56dp</dimen>
+ <dimen name="wallet_screen_header_icon_size">56dp</dimen>
+ <dimen name="wallet_screen_header_view_size">80dp</dimen>
<dimen name="card_margin">16dp</dimen>
<dimen name="card_carousel_dot_offset">24dp</dimen>
<dimen name="card_carousel_dot_unselected_radius">2dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 30add20..150fc73 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -689,12 +689,12 @@
<string name="accessibility_quick_settings_hotspot_changed_on">Mobile hotspot turned on.</string>
<!-- Announcement made when the screen stopped casting (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_casting_turned_off">Screen casting stopped.</string>
- <!-- Content description of the work mode title in quick settings when off (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <!-- Content description of the work mode title in quick settings when off (not shown on the screen). Paused is used as an adjective [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_work_mode_off">Work mode paused.</string>
<!-- Content description of the work mode title in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_work_mode_on">Work mode on.</string>
- <!-- Announcement made when the work mode changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_work_mode_changed_off">Work mode turned paused.</string>
+ <!-- Announcement made when the work mode changes to off (not shown on the screen). Paused is used as a verb. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_work_mode_changed_off">Work mode paused.</string>
<!-- Announcement made when the work mode changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_work_mode_changed_on">Work mode turned on.</string>
<!-- Announcement made when the Data Saver changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -2828,7 +2828,7 @@
<!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] -->
<string name="controls_media_settings_button">Settings</string>
- <!-- Title for Smartspace recommendation card within media controls [CHAR_LIMIT=50] -->
+ <!-- Title for Smartspace recommendation card within media controls. The "Play" means the action to play a media [CHAR_LIMIT=10] -->
<string name="controls_media_smartspace_rec_title">Play</string>
<!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 10f7e40..9fa63dd 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -660,9 +660,12 @@
<item name="android:tint">?android:attr/colorAccent</item>
</style>
+ <style name="MediaPlayer.AppIcon.Recommendation" parent="MediaPlayer.AppIcon">
+ <item name="android:tint">@color/transparent</item>
+ </style>
+
<style name="MediaPlayer.Album">
<item name="android:backgroundTint">@color/media_player_album_bg</item>
-
</style>
<!-- Used to style charging animation AVD animation -->
@@ -692,6 +695,7 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowLightNavigationBar">true</item>
+ <item name="android:windowActivityTransitions">true</item>
</style>
<!-- Privacy dialog -->
@@ -874,12 +878,12 @@
<style name="Wallet.TextAppearance">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">@color/wallet_primary_text</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:singleLine">true</item>
<item name="android:textSize">14sp</item>
</style>
- <style name="Wallet.Theme">
- <item name="android:colorControlHighlight">@*android:color/primary_text_material_dark</item>
+ <style name="Wallet.Theme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:colorBackground">@android:color/system_neutral1_900</item>
</style>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 927bce0..2cf3ad2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -105,6 +105,8 @@
public static final int SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1 << 17;
// The IME is showing
public static final int SYSUI_STATE_IME_SHOWING = 1 << 18;
+ // The window magnification is overlapped with system gesture insets at the bottom.
+ public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -125,7 +127,8 @@
SYSUI_STATE_GLOBAL_ACTIONS_SHOWING,
SYSUI_STATE_ONE_HANDED_ACTIVE,
SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
- SYSUI_STATE_IME_SHOWING
+ SYSUI_STATE_IME_SHOWING,
+ SYSUI_STATE_MAGNIFICATION_OVERLAP
})
public @interface SystemUiStateFlags {}
@@ -153,6 +156,7 @@
str.add((flags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
? "allow_gesture" : "");
str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : "");
+ str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : "");
return str.toString();
}
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 76cec0b..64a683e 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -305,10 +305,15 @@
continue;
}
Rect subImage = new Rect(
- Math.round(area.left * b.getWidth()),
- Math.round(area.top * b.getHeight()),
- Math.round(area.right * b.getWidth()),
- Math.round(area.bottom * b.getHeight()));
+ (int) Math.floor(area.left * b.getWidth()),
+ (int) Math.floor(area.top * b.getHeight()),
+ (int) Math.ceil(area.right * b.getWidth()),
+ (int) Math.ceil(area.bottom * b.getHeight()));
+ if (subImage.isEmpty()) {
+ // Do not notify client. treat it as too small to sample
+ colors.add(null);
+ continue;
+ }
Bitmap colorImg = Bitmap.createBitmap(b,
subImage.left, subImage.top, subImage.width(), subImage.height());
WallpaperColors color = WallpaperColors.fromBitmap(colorImg);
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 7f18379..bf09975 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -73,7 +73,8 @@
Key.TOUCHED_RINGER_TOGGLE,
Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP,
Key.HAS_SEEN_REVERSE_BOTTOM_SHEET,
- Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
+ Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT,
+ Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP
})
// TODO: annotate these with their types so {@link PrefsCommandLine} can know how to set them
public @interface Key {
@@ -122,6 +123,8 @@
String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
String HAS_SEEN_REVERSE_BOTTOM_SHEET = "HasSeenReverseBottomSheet";
String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
+ String HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP =
+ "HasSeenAccessibilityFloatingMenuDockTooltip";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
index b69001d..c472457 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
@@ -63,17 +63,15 @@
}
/**
- * Gets the object by the element index.
+ * Returns the object with the given display id.
*
- * <p> If the index is bigger than the array size, an {@link ArrayIndexOutOfBoundsException} is
- * thrown for apps targeting {@link android.os.Build.VERSION_CODES#Q} and later </p>
*
- * @param index the element index
+ * @param displayId the logical display Id
* @return T
- * @see SparseArray#valueAt(int)
*/
- public T valueAt(int index) {
- return mSparseArray.valueAt(index);
+ @Nullable
+ public T valueAt(int displayId) {
+ return mSparseArray.get(displayId);
}
@NonNull
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
index 4c892e29..4b30ec3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
@@ -113,7 +113,7 @@
final float rawX = event.getRawX();
final float rawY = event.getRawY();
boolean handled = false;
- switch (event.getAction()) {
+ switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mPointerDown.set(rawX, rawY);
mHandler.postAtTime(mCancelTapGestureRunnable,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 4f5fdc9..cee395b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -18,10 +18,11 @@
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -37,9 +38,13 @@
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
import javax.inject.Inject;
/**
@@ -52,34 +57,33 @@
public class WindowMagnification extends SystemUI implements WindowMagnifierCallback,
CommandQueue.Callbacks {
private static final String TAG = "WindowMagnification";
- private static final int CONFIG_MASK =
- ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION
- | ActivityInfo.CONFIG_LOCALE;
private final ModeSwitchesController mModeSwitchesController;
private final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
private final CommandQueue mCommandQueue;
+ private final OverviewProxyService mOverviewProxyService;
private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
private Configuration mLastConfiguration;
+ private SysUiState mSysUiState;
private static class AnimationControllerSupplier extends
DisplayIdIndexSupplier<WindowMagnificationAnimationController> {
private final Context mContext;
private final Handler mHandler;
- private final NavigationModeController mNavigationModeController;
private final WindowMagnifierCallback mWindowMagnifierCallback;
+ private final SysUiState mSysUiState;
AnimationControllerSupplier(Context context, Handler handler,
- NavigationModeController navigationModeController,
- WindowMagnifierCallback windowMagnifierCallback, DisplayManager displayManager) {
+ WindowMagnifierCallback windowMagnifierCallback,
+ DisplayManager displayManager, SysUiState sysUiState) {
super(displayManager);
mContext = context;
mHandler = handler;
- mNavigationModeController = navigationModeController;
mWindowMagnifierCallback = windowMagnifierCallback;
+ mSysUiState = sysUiState;
}
@Override
@@ -89,10 +93,7 @@
final WindowMagnificationController controller = new WindowMagnificationController(
mContext,
mHandler, new SfVsyncFrameCallbackProvider(), null,
- new SurfaceControl.Transaction(), mWindowMagnifierCallback);
- final int navBarMode = mNavigationModeController.addListener(
- controller::onNavigationModeChanged);
- controller.onNavigationModeChanged(navBarMode);
+ new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState);
return new WindowMagnificationAnimationController(windowContext, controller);
}
}
@@ -103,24 +104,22 @@
@Inject
public WindowMagnification(Context context, @Main Handler mainHandler,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
- NavigationModeController navigationModeController) {
+ SysUiState sysUiState, OverviewProxyService overviewProxyService) {
super(context);
mHandler = mainHandler;
mLastConfiguration = new Configuration(context.getResources().getConfiguration());
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mCommandQueue = commandQueue;
mModeSwitchesController = modeSwitchesController;
+ mSysUiState = sysUiState;
+ mOverviewProxyService = overviewProxyService;
mAnimationControllerSupplier = new AnimationControllerSupplier(context,
- mHandler, navigationModeController, this,
- context.getSystemService(DisplayManager.class));
+ mHandler, this, context.getSystemService(DisplayManager.class), sysUiState);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
final int configDiff = newConfig.diff(mLastConfiguration);
- if ((configDiff & CONFIG_MASK) == 0) {
- return;
- }
mLastConfiguration.setTo(newConfig);
mAnimationControllerSupplier.forEach(
animationController -> animationController.onConfigurationChanged(configDiff));
@@ -132,6 +131,28 @@
@Override
public void start() {
mCommandQueue.addCallback(this);
+ mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ if (isConnected) {
+ updateSysUiStateFlag();
+ }
+ }
+ });
+ }
+
+ private void updateSysUiStateFlag() {
+ //TODO(b/187510533): support multi-display once SysuiState supports it.
+ final WindowMagnificationAnimationController controller =
+ mAnimationControllerSupplier.valueAt(Display.DEFAULT_DISPLAY);
+ if (controller != null) {
+ controller.updateSysUiStateFlag();
+ } else {
+ // The instance is initialized when there is an IPC request. Considering
+ // self-crash cases, we need to reset the flag in such situation.
+ mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
+ .commitUpdate(Display.DEFAULT_DISPLAY);
+ }
}
@MainThread
@@ -210,6 +231,13 @@
}
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(TAG);
+ mAnimationControllerSupplier.forEach(
+ animationController -> animationController.dump(pw));
+ }
+
private void setWindowMagnificationConnection() {
if (mWindowMagnificationConnectionImpl == null) {
mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 5758b15..36fef3e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -31,6 +31,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -269,6 +270,14 @@
mController.enableWindowMagnification(sentScale, centerX, centerY);
}
+ public void updateSysUiStateFlag() {
+ mController.updateSysUIStateFlag();
+ }
+
+ void dump(PrintWriter pw) {
+ mController.dump(pw);
+ }
+
private static ValueAnimator newValueAnimator(Resources resources) {
final ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index ae16703..fcb090a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -16,9 +16,10 @@
package com.android.systemui.accessibility;
+import static android.view.WindowInsets.Type.systemGestures;
import static android.view.WindowManager.LayoutParams;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
@@ -28,6 +29,7 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -52,14 +54,17 @@
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.Locale;
@@ -111,6 +116,7 @@
private final View.OnLayoutChangeListener mMirrorSurfaceViewLayoutChangeListener;
private final Runnable mMirrorViewRunnable;
private final Runnable mUpdateStateDescriptionRunnable;
+ private final Runnable mWindowInsetChangeRunnable;
private View mMirrorView;
private SurfaceView mMirrorSurfaceView;
private int mMirrorSurfaceMargin;
@@ -119,9 +125,8 @@
private int mOuterBorderSize;
// The boundary of magnification frame.
private final Rect mMagnificationFrameBoundary = new Rect();
-
- private int mNavBarMode;
- private int mNavGestureHeight;
+ // The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid.
+ private int mSystemGestureTop = -1;
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
private final MagnificationGestureDetector mGestureDetector;
@@ -130,6 +135,9 @@
private Locale mLocale;
private NumberFormat mPercentFormat;
private float mBounceEffectAnimationScale;
+ private SysUiState mSysUiState;
+ // Set it to true when the view is overlapped with the gesture insets at the bottom.
+ private boolean mOverlapWithGestureInsets;
@Nullable
private MirrorWindowControl mMirrorWindowControl;
@@ -137,11 +145,12 @@
WindowMagnificationController(@UiContext Context context, @NonNull Handler handler,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
- @NonNull WindowMagnifierCallback callback) {
+ @NonNull WindowMagnifierCallback callback, SysUiState sysUiState) {
mContext = context;
mHandler = handler;
mSfVsyncFrameProvider = sfVsyncFrameProvider;
mWindowMagnifierCallback = callback;
+ mSysUiState = sysUiState;
final Display display = mContext.getDisplay();
mDisplayId = mContext.getDisplayId();
@@ -170,13 +179,18 @@
mMirrorViewRunnable = () -> {
if (mMirrorView != null) {
mMirrorView.getBoundsOnScreen(mMirrorViewBounds);
+ updateSystemUIStateIfNeeded();
mWindowMagnifierCallback.onWindowMagnifierBoundsChanged(
mDisplayId, mMirrorViewBounds);
}
};
mMirrorViewLayoutChangeListener =
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ if (!mHandler.hasCallbacks(mMirrorViewRunnable)) {
mHandler.post(mMirrorViewRunnable);
+ }
+ };
+
mMirrorSurfaceViewLayoutChangeListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
-> applyTapExcludeRegion();
@@ -199,6 +213,7 @@
mMirrorView.setStateDescription(formatStateDescription(mScale));
}
};
+ mWindowInsetChangeRunnable = this::onWindowInsetChanged;
}
private void updateDimensions() {
@@ -210,7 +225,6 @@
R.dimen.magnification_drag_view_size);
mOuterBorderSize = mResources.getDimensionPixelSize(
R.dimen.magnification_outer_border_margin);
- updateNavigationBarDimensions();
}
private void computeBounceAnimationScale() {
@@ -220,16 +234,16 @@
mBounceEffectAnimationScale = Math.min(animationScaleMax, ANIMATION_BOUNCE_EFFECT_SCALE);
}
- private void updateNavigationBarDimensions() {
- if (!supportsSwipeUpGesture()) {
- mNavGestureHeight = 0;
- return;
+ private boolean updateSystemGestureInsetsTop() {
+ final WindowMetrics windowMetrics = mWm.getCurrentWindowMetrics();
+ final Insets insets = windowMetrics.getWindowInsets().getInsets(systemGestures());
+ final int gestureTop =
+ insets.bottom != 0 ? windowMetrics.getBounds().bottom - insets.bottom : -1;
+ if (gestureTop != mSystemGestureTop) {
+ mSystemGestureTop = gestureTop;
+ return true;
}
- mNavGestureHeight = (mWindowBounds.width() > mWindowBounds.height())
- ? mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height_landscape)
- : mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_gesture_height);
+ return false;
}
/**
@@ -255,6 +269,7 @@
if (mMirrorWindowControl != null) {
mMirrorWindowControl.destroyControl();
}
+ updateSystemUIStateIfNeeded();
}
/**
@@ -277,6 +292,10 @@
}
}
+ private void updateSystemUIStateIfNeeded() {
+ updateSysUIState(false);
+ }
+
private void updateAccessibilityWindowTitleIfNeeded() {
if (!isWindowVisible()) return;
LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams();
@@ -284,13 +303,6 @@
mWm.updateViewLayout(mMirrorView, params);
}
- /** Handles MirrorWindow position when the navigation bar mode changed. */
- public void onNavigationModeChanged(int mode) {
- mNavBarMode = mode;
- updateNavigationBarDimensions();
- updateMirrorViewLayout();
- }
-
/** Handles MirrorWindow position when the device rotation changed. */
private void onRotate() {
final Display display = mContext.getDisplay();
@@ -299,7 +311,6 @@
setMagnificationFrameBoundary();
mRotation = display.getRotation();
- updateNavigationBarDimensions();
if (!isWindowVisible()) {
return;
@@ -351,6 +362,7 @@
params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
params.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ params.receiveInsetsIgnoringZOrder = true;
params.setTitle(mContext.getString(R.string.magnification_window_title));
params.accessibilityTitle = getAccessibilityWindowTitle();
@@ -368,6 +380,12 @@
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
mMirrorView.addOnLayoutChangeListener(mMirrorViewLayoutChangeListener);
mMirrorView.setAccessibilityDelegate(new MirrorWindowA11yDelegate());
+ mMirrorView.setOnApplyWindowInsetsListener((v, insets) -> {
+ if (!mHandler.hasCallbacks(mWindowInsetChangeRunnable)) {
+ mHandler.post(mWindowInsetChangeRunnable);
+ }
+ return v.onApplyWindowInsets(insets);
+ });
mWm.addView(mMirrorView, params);
@@ -377,6 +395,12 @@
addDragTouchListeners();
}
+ private void onWindowInsetChanged() {
+ if (updateSystemGestureInsetsTop()) {
+ updateSystemUIStateIfNeeded();
+ }
+ }
+
private void applyTapExcludeRegion() {
final Region tapExcludeRegion = calculateTapExclude();
final IWindow window = IWindow.Stub.asInterface(mMirrorView.getWindowToken());
@@ -464,17 +488,12 @@
return;
}
final int maxMirrorViewX = mWindowBounds.width() - mMirrorView.getWidth();
- final int maxMirrorViewY =
- mWindowBounds.height() - mMirrorView.getHeight() - mNavGestureHeight;
+ final int maxMirrorViewY = mWindowBounds.height() - mMirrorView.getHeight();
+
LayoutParams params =
(LayoutParams) mMirrorView.getLayoutParams();
params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
- // If nav bar mode supports swipe-up gesture, the Y position of mirror view should not
- // overlap nav bar window to prevent window-dragging obscured.
- if (supportsSwipeUpGesture()) {
- params.y = Math.min(params.y, maxMirrorViewY);
- }
// Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView
// able to move close to the screen edges.
@@ -508,6 +527,10 @@
return false;
}
+ public void updateSysUIStateFlag() {
+ updateSysUIState(true);
+ }
+
/**
* Calculates the desired source bounds. This will be the area under from the center of the
* displayFrame, factoring in scale.
@@ -569,6 +592,16 @@
return false;
}
+ private void updateSysUIState(boolean force) {
+ final boolean overlap = isWindowVisible() && mSystemGestureTop > 0
+ && mMirrorViewBounds.bottom > mSystemGestureTop;
+ if (force || overlap != mOverlapWithGestureInsets) {
+ mOverlapWithGestureInsets = overlap;
+ mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, mOverlapWithGestureInsets)
+ .commitUpdate(mDisplayId);
+ }
+ }
+
@Override
public void surfaceCreated(SurfaceHolder holder) {
createMirror();
@@ -676,10 +709,6 @@
return mMirrorView != null;
}
- private boolean supportsSwipeUpGesture() {
- return mNavBarMode == NAV_BAR_MODE_2BUTTON || mNavBarMode == NAV_BAR_MODE_GESTURAL;
- }
-
private CharSequence formatStateDescription(float scale) {
// Cache the locale-appropriate NumberFormat. Configuration locale is guaranteed
// non-null, so the first time this is called we will always get the appropriate
@@ -722,6 +751,14 @@
scaleAnimator.start();
}
+ public void dump(PrintWriter pw) {
+ pw.println("WindowMagnificationController (displayId=" + mDisplayId + "):");
+ pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
+ pw.println(" mScale:" + mScale);
+ pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
+ pw.println(" mSystemGestureTop:" + mSystemGestureTop);
+ }
+
private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index 3b3bad3..ee62768 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -18,11 +18,13 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
+import static com.android.systemui.Prefs.Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP;
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.ShapeType;
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.SizeType;
@@ -34,15 +36,19 @@
import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Prefs;
/**
* Contains logic for an accessibility floating menu view.
*/
public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
- private static final int DEFAULT_FADE_EFFECT_ENABLED = 1;
+ private static final int DEFAULT_FADE_EFFECT_IS_ENABLED = 1;
+ private static final int DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED = 0;
private static final float DEFAULT_OPACITY_VALUE = 0.55f;
private final Context mContext;
private final AccessibilityFloatingMenuView mMenuView;
+ private final MigrationTooltipView mMigrationTooltipView;
+ private final DockTooltipView mDockTooltipView;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ContentObserver mContentObserver =
@@ -86,6 +92,8 @@
AccessibilityFloatingMenu(Context context, AccessibilityFloatingMenuView menuView) {
mContext = context;
mMenuView = menuView;
+ mMigrationTooltipView = new MigrationTooltipView(mContext, mMenuView);
+ mDockTooltipView = new DockTooltipView(mContext, mMenuView);
}
@Override
@@ -105,6 +113,9 @@
getOpacityValue(mContext));
mMenuView.setSizeType(getSizeType(mContext));
mMenuView.setShapeType(getShapeType(mContext));
+ mMenuView.setOnDragEndListener(this::showDockTooltipIfNecessary);
+
+ showMigrationTooltipIfNecessary();
registerContentObservers();
}
@@ -116,14 +127,48 @@
}
mMenuView.hide();
+ mMigrationTooltipView.hide();
+ mDockTooltipView.hide();
unregisterContentObservers();
}
+ // Migration tooltip was the android S feature. It's just used on the Android version from R
+ // to S. In addition, it only shows once.
+ private void showMigrationTooltipIfNecessary() {
+ if (isMigrationTooltipPromptEnabled(mContext)) {
+ mMigrationTooltipView.show();
+
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT, /* disabled */ 0);
+ }
+ }
+
+ private static boolean isMigrationTooltipPromptEnabled(Context context) {
+ return Settings.Secure.getInt(
+ context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
+ DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED) == /* enabled */ 1;
+ }
+
+ /**
+ * Shows tooltip when user drags accessibility floating menu for the first time.
+ */
+ private void showDockTooltipIfNecessary() {
+ if (!Prefs.get(mContext).getBoolean(
+ HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, false)) {
+ // if the menu is an oval, the user has already dragged it out, so show the tooltip.
+ if (mMenuView.isOvalShape()) {
+ mDockTooltipView.show();
+ }
+
+ Prefs.putBoolean(mContext, HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, true);
+ }
+ }
+
private static boolean isFadeEffectEnabled(Context context) {
return Settings.Secure.getInt(
context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
- DEFAULT_FADE_EFFECT_ENABLED) == /* enable */ 1;
+ DEFAULT_FADE_EFFECT_IS_ENABLED) == /* enabled */ 1;
}
private static float getOpacityValue(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 934e20d..49e6b47 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -19,6 +19,8 @@
import static android.util.MathUtils.constrain;
import static android.util.MathUtils.sq;
+import static java.util.Objects.requireNonNull;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -43,7 +45,9 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.animation.Animation;
import android.view.animation.OvershootInterpolator;
+import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import androidx.annotation.DimenRes;
@@ -60,6 +64,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
/**
* Accessibility floating menu is used for the actions of accessibility features, it's also the
@@ -78,6 +83,10 @@
private static final int MIN_WINDOW_Y = 0;
private static final float LOCATION_Y_PERCENTAGE = 0.8f;
+ private static final int ANIMATION_START_OFFSET = 600;
+ private static final int ANIMATION_DURATION_MS = 600;
+ private static final float ANIMATION_TO_X_VALUE = 0.5f;
+
private boolean mIsFadeEffectEnabled;
private boolean mIsShowing;
private boolean mIsDownInEnlargedTouchArea;
@@ -107,6 +116,7 @@
private float mPercentageY = LOCATION_Y_PERCENTAGE;
private float mSquareScaledTouchSlop;
private final Configuration mLastConfiguration;
+ private Optional<OnDragEndListener> mOnDragEndListener = Optional.empty();
private final RecyclerView mListView;
private final AccessibilityTargetAdapter mAdapter;
private float mFadeOutValue;
@@ -161,6 +171,17 @@
int RIGHT = 1;
}
+ /**
+ * Interface for a callback to be invoked when the floating menu was dragging.
+ */
+ interface OnDragEndListener {
+
+ /**
+ * Invoked when the floating menu has dragged end.
+ */
+ void onDragEnd();
+ }
+
public AccessibilityFloatingMenuView(Context context) {
this(context, new RecyclerView(context));
}
@@ -201,6 +222,8 @@
updateRadiusWith(mSizeType, mRadiusType, mTargets.size());
fadeOut();
+
+ mOnDragEndListener.ifPresent(OnDragEndListener::onDragEnd);
}
});
@@ -266,7 +289,7 @@
// Must switch the oval shape type before tapping the corresponding item in the
// list view, otherwise it can't work on it.
- if (mShapeType == ShapeType.HALF_OVAL) {
+ if (!isOvalShape()) {
setShapeType(ShapeType.OVAL);
return true;
@@ -299,10 +322,6 @@
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
- if (super.performAccessibilityAction(action, arguments)) {
- return true;
- }
-
fadeIn();
final Rect bounds = getAvailableBounds();
@@ -340,7 +359,7 @@
return true;
}
- return false;
+ return super.performAccessibilityAction(action, arguments);
}
void show() {
@@ -367,6 +386,10 @@
return mIsShowing;
}
+ boolean isOvalShape() {
+ return mShapeType == ShapeType.OVAL;
+ }
+
void onTargetsChanged(List<AccessibilityTarget> newTargets) {
fadeIn();
@@ -411,6 +434,41 @@
fadeOut();
}
+ public void setOnDragEndListener(OnDragEndListener onDragListener) {
+ mOnDragEndListener = Optional.ofNullable(onDragListener);
+ }
+
+ void startTranslateXAnimation() {
+ fadeIn();
+
+ final float toXValue = mAlignment == Alignment.RIGHT
+ ? ANIMATION_TO_X_VALUE
+ : -ANIMATION_TO_X_VALUE;
+ final TranslateAnimation animation =
+ new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
+ Animation.RELATIVE_TO_SELF, toXValue,
+ Animation.RELATIVE_TO_SELF, 0,
+ Animation.RELATIVE_TO_SELF, 0);
+ animation.setDuration(ANIMATION_DURATION_MS);
+ animation.setRepeatMode(Animation.REVERSE);
+ animation.setInterpolator(new OvershootInterpolator());
+ animation.setRepeatCount(Animation.INFINITE);
+ animation.setStartOffset(ANIMATION_START_OFFSET);
+ mListView.startAnimation(animation);
+ }
+
+ void stopTranslateXAnimation() {
+ mListView.clearAnimation();
+
+ fadeOut();
+ }
+
+ Rect getWindowLocationOnScreen() {
+ final int left = mCurrentLayoutParams.x;
+ final int top = mCurrentLayoutParams.y;
+ return new Rect(left, top, left + getWindowWidth(), top + getWindowHeight());
+ }
+
void updateOpacityWith(boolean isFadeEffectEnabled, float newOpacityValue) {
mIsFadeEffectEnabled = isFadeEffectEnabled;
mFadeOutValue = newOpacityValue;
@@ -534,11 +592,7 @@
}
private Handler createUiHandler() {
- final Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalArgumentException("looper must not be null");
- }
- return new Handler(looper);
+ return new Handler(requireNonNull(Looper.myLooper(), "looper must not be null"));
}
private void updateDimensions() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
new file mode 100644
index 0000000..d8e80fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import android.text.Annotation;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.style.ClickableSpan;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * A span that turns the text wrapped by annotation tag into the clickable link text.
+ */
+class AnnotationLinkSpan extends ClickableSpan {
+ private final Optional<View.OnClickListener> mClickListener;
+
+ private AnnotationLinkSpan(View.OnClickListener listener) {
+ mClickListener = Optional.ofNullable(listener);
+ }
+
+ @Override
+ public void onClick(View view) {
+ mClickListener.ifPresent(listener -> listener.onClick(view));
+ }
+
+ /**
+ * Makes the text has the link with the click action. In addition, the span will match first
+ * LinkInfo and attach into the text.
+ *
+ * @param text the text wrapped by annotation tag
+ * @param linkInfos used to attach the click action into the corresponding span
+ * @return the text attached with the span
+ */
+ static CharSequence linkify(CharSequence text, LinkInfo... linkInfos) {
+ final SpannableString msg = new SpannableString(text);
+ final Annotation[] spans =
+ msg.getSpans(/* queryStart= */ 0, msg.length(), Annotation.class);
+ final SpannableStringBuilder builder = new SpannableStringBuilder(msg);
+
+ Arrays.asList(spans).forEach(annotationTag -> {
+ final String key = annotationTag.getValue();
+ final Optional<LinkInfo> linkInfo =
+ Arrays.asList(linkInfos).stream().filter(
+ info -> info.mAnnotation.isPresent()
+ && info.mAnnotation.get().equals(key)).findFirst();
+
+ linkInfo.flatMap(info -> info.mListener).ifPresent(listener -> {
+ final AnnotationLinkSpan span = new AnnotationLinkSpan(listener);
+ builder.setSpan(span,
+ msg.getSpanStart(annotationTag),
+ msg.getSpanEnd(annotationTag),
+ msg.getSpanFlags(span));
+ });
+ });
+
+ return builder;
+ }
+
+ /**
+ * Data class to store the annotation and the click action.
+ */
+ static class LinkInfo {
+ static final String DEFAULT_ANNOTATION = "link";
+ private final Optional<String> mAnnotation;
+ private final Optional<View.OnClickListener> mListener;
+
+ LinkInfo(@NonNull String annotation, View.OnClickListener listener) {
+ mAnnotation = Optional.of(annotation);
+ mListener = Optional.ofNullable(listener);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java
new file mode 100644
index 0000000..1abf559
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static android.util.TypedValue.COMPLEX_UNIT_PX;
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import android.annotation.UiContext;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Bundle;
+import android.text.method.MovementMethod;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.recents.TriangleShape;
+
+/**
+ * Base tooltip view that shows the information about the operation of the
+ * Accessibility floating menu. In addition, the anchor view is only for {@link
+ * AccessibilityFloatingMenuView}, it should be more suited for displaying one-off menus to avoid
+ * the performance hit for the extra window.
+ */
+class BaseTooltipView extends FrameLayout {
+ private int mFontSize;
+ private int mTextViewMargin;
+ private int mTextViewPadding;
+ private int mTextViewCornerRadius;
+ private int mArrowMargin;
+ private int mArrowWidth;
+ private int mArrowHeight;
+ private int mArrowCornerRadius;
+ private int mScreenWidth;
+ private boolean mIsShowing;
+ private TextView mTextView;
+ private final WindowManager.LayoutParams mCurrentLayoutParams;
+ private final WindowManager mWindowManager;
+ private final AccessibilityFloatingMenuView mAnchorView;
+
+ BaseTooltipView(@UiContext Context context, AccessibilityFloatingMenuView anchorView) {
+ super(context);
+ mWindowManager = context.getSystemService(WindowManager.class);
+ mAnchorView = anchorView;
+ mCurrentLayoutParams = createDefaultLayoutParams();
+
+ updateDimensions();
+ initViews();
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ updateDimensions();
+ updateTextView();
+
+ mAnchorView.onConfigurationChanged(newConfig);
+ final Rect anchorViewLocation = mAnchorView.getWindowLocationOnScreen();
+ updateArrowWith(anchorViewLocation);
+ updateWidthWith(anchorViewLocation);
+ updateLocationWith(anchorViewLocation);
+
+ mWindowManager.updateViewLayout(this, mCurrentLayoutParams);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ hide();
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+
+ info.addAction(AccessibilityAction.ACTION_DISMISS);
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (action == AccessibilityAction.ACTION_DISMISS.getId()) {
+ hide();
+ return true;
+ }
+
+ return super.performAccessibilityAction(action, arguments);
+ }
+
+ void show() {
+ if (isShowing()) {
+ return;
+ }
+
+ mIsShowing = true;
+ final Rect anchorViewLocation = mAnchorView.getWindowLocationOnScreen();
+ updateArrowWith(anchorViewLocation);
+ updateWidthWith(anchorViewLocation);
+ updateLocationWith(anchorViewLocation);
+
+ mWindowManager.addView(this, mCurrentLayoutParams);
+ }
+
+ void hide() {
+ if (!isShowing()) {
+ return;
+ }
+
+ mIsShowing = false;
+ mWindowManager.removeView(this);
+ }
+
+ void setDescription(CharSequence text) {
+ mTextView.setText(text);
+ }
+
+ void setMovementMethod(MovementMethod movement) {
+ mTextView.setMovementMethod(movement);
+ }
+
+ private boolean isShowing() {
+ return mIsShowing;
+ }
+
+ private void initViews() {
+ final View contentView =
+ LayoutInflater.from(getContext()).inflate(
+ R.layout.accessibility_floating_menu_tooltip, this, false);
+
+ mTextView = contentView.findViewById(R.id.text);
+
+ addView(contentView);
+ }
+
+ private static WindowManager.LayoutParams createDefaultLayoutParams() {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ params.windowAnimations = android.R.style.Animation_Translucent;
+ params.gravity = Gravity.START | Gravity.TOP;
+
+ return params;
+ }
+
+ private void updateDimensions() {
+ final Resources res = getResources();
+ final DisplayMetrics dm = res.getDisplayMetrics();
+ mScreenWidth = dm.widthPixels;
+ mArrowWidth =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_arrow_width);
+ mArrowHeight =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_arrow_height);
+ mArrowMargin =
+ res.getDimensionPixelSize(
+ R.dimen.accessibility_floating_tooltip_arrow_margin);
+ mArrowCornerRadius =
+ res.getDimensionPixelSize(
+ R.dimen.accessibility_floating_tooltip_arrow_corner_radius);
+ mFontSize =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_font_size);
+ mTextViewMargin =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_margin);
+ mTextViewPadding =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_padding);
+ mTextViewCornerRadius =
+ res.getDimensionPixelSize(
+ R.dimen.accessibility_floating_tooltip_text_corner_radius);
+ }
+
+ private void updateTextView() {
+ mTextView.setTextSize(COMPLEX_UNIT_PX, mFontSize);
+ mTextView.setPadding(mTextViewPadding, mTextViewPadding, mTextViewPadding,
+ mTextViewPadding);
+
+ final GradientDrawable gradientDrawable = (GradientDrawable) mTextView.getBackground();
+ gradientDrawable.setCornerRadius(mTextViewCornerRadius);
+ }
+
+ private void updateArrowWith(Rect anchorViewLocation) {
+ final boolean isAnchorViewOnLeft = isAnchorViewOnLeft(anchorViewLocation);
+ final View arrowView = findViewById(isAnchorViewOnLeft
+ ? R.id.arrow_left
+ : R.id.arrow_right);
+ arrowView.setVisibility(VISIBLE);
+ drawArrow(arrowView, isAnchorViewOnLeft);
+
+ final LinearLayout.LayoutParams layoutParams =
+ (LinearLayout.LayoutParams) arrowView.getLayoutParams();
+ layoutParams.width = mArrowWidth;
+ layoutParams.height = mArrowHeight;
+
+ final int leftMargin = isAnchorViewOnLeft ? 0 : mArrowMargin;
+ final int rightMargin = isAnchorViewOnLeft ? mArrowMargin : 0;
+ layoutParams.setMargins(leftMargin, 0, rightMargin, 0);
+ arrowView.setLayoutParams(layoutParams);
+ }
+
+ private void updateWidthWith(Rect anchorViewLocation) {
+ final ViewGroup.LayoutParams layoutParams = mTextView.getLayoutParams();
+ layoutParams.width = getTextWidthWith(anchorViewLocation);
+ mTextView.setLayoutParams(layoutParams);
+ }
+
+ private void updateLocationWith(Rect anchorViewLocation) {
+ mCurrentLayoutParams.x = isAnchorViewOnLeft(anchorViewLocation)
+ ? anchorViewLocation.width()
+ : mScreenWidth - getWindowWidthWith(anchorViewLocation)
+ - anchorViewLocation.width();
+ mCurrentLayoutParams.y =
+ anchorViewLocation.centerY() - (getTextHeightWith(anchorViewLocation) / 2);
+ }
+
+ private void drawArrow(View view, boolean isPointingLeft) {
+ final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ final TriangleShape triangleShape =
+ TriangleShape.createHorizontal(layoutParams.width, layoutParams.height,
+ isPointingLeft);
+ final ShapeDrawable arrowDrawable = new ShapeDrawable(triangleShape);
+ final Paint arrowPaint = arrowDrawable.getPaint();
+ arrowPaint.setColor(Utils.getColorAttrDefaultColor(getContext(),
+ com.android.internal.R.attr.colorAccentPrimary));
+ final CornerPathEffect effect = new CornerPathEffect(mArrowCornerRadius);
+ arrowPaint.setPathEffect(effect);
+ view.setBackground(arrowDrawable);
+ }
+
+ private boolean isAnchorViewOnLeft(Rect anchorViewLocation) {
+ return anchorViewLocation.left < (mScreenWidth / 2);
+ }
+
+ private int getTextWidthWith(Rect anchorViewLocation) {
+ final int widthSpec =
+ MeasureSpec.makeMeasureSpec(getAvailableTextWidthWith(anchorViewLocation), AT_MOST);
+ final int heightSpec =
+ MeasureSpec.makeMeasureSpec(0, UNSPECIFIED);
+ mTextView.measure(widthSpec, heightSpec);
+ return mTextView.getMeasuredWidth();
+ }
+
+ private int getTextHeightWith(Rect anchorViewLocation) {
+ final int widthSpec =
+ MeasureSpec.makeMeasureSpec(getAvailableTextWidthWith(anchorViewLocation), AT_MOST);
+ final int heightSpec =
+ MeasureSpec.makeMeasureSpec(0, UNSPECIFIED);
+ mTextView.measure(widthSpec, heightSpec);
+ return mTextView.getMeasuredHeight();
+ }
+
+ private int getAvailableTextWidthWith(Rect anchorViewLocation) {
+ return mScreenWidth - anchorViewLocation.width() - mArrowWidth - mArrowMargin
+ - mTextViewMargin;
+ }
+
+ private int getWindowWidthWith(Rect anchorViewLocation) {
+ return getTextWidthWith(anchorViewLocation) + mArrowWidth + mArrowMargin;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java
new file mode 100644
index 0000000..49056a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import android.content.Context;
+
+import com.android.systemui.R;
+
+/**
+ * Dock tooltip view that shows the info about moving the Accessibility button to the edge to hide.
+ */
+class DockTooltipView extends BaseTooltipView {
+ private final AccessibilityFloatingMenuView mAnchorView;
+
+ DockTooltipView(Context context, AccessibilityFloatingMenuView anchorView) {
+ super(context, anchorView);
+ mAnchorView = anchorView;
+
+ setDescription(
+ getContext().getText(R.string.accessibility_floating_button_docking_tooltip));
+ }
+
+ @Override
+ void hide() {
+ super.hide();
+
+ mAnchorView.stopTranslateXAnimation();
+ }
+
+ @Override
+ void show() {
+ super.show();
+
+ mAnchorView.startTranslateXAnimation();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java
new file mode 100644
index 0000000..e4f3e31
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_BUTTON_COMPONENT_NAME;
+
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.text.method.LinkMovementMethod;
+
+import com.android.systemui.R;
+
+/**
+ * Migration tooltip view that shows the information about the Accessibility button was replaced
+ * with the floating menu.
+ */
+class MigrationTooltipView extends BaseTooltipView {
+ MigrationTooltipView(Context context, AccessibilityFloatingMenuView anchorView) {
+ super(context, anchorView);
+
+ final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
+ ACCESSIBILITY_BUTTON_COMPONENT_NAME.flattenToShortString());
+
+ final AnnotationLinkSpan.LinkInfo linkInfo = new AnnotationLinkSpan.LinkInfo(
+ AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
+ v -> {
+ getContext().startActivity(intent);
+ hide();
+ });
+
+ final int textResId = R.string.accessibility_floating_button_migration_tooltip;
+ setDescription(AnnotationLinkSpan.linkify(getContext().getText(textResId), linkInfo));
+ setMovementMethod(LinkMovementMethod.getInstance());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 5a50f0e..875bfdb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -59,7 +59,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
-import com.android.systemui.biometrics.HbmTypes.HbmType;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
@@ -72,6 +71,8 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import java.util.Optional;
+
import javax.inject.Inject;
/**
@@ -87,7 +88,7 @@
*/
@SuppressWarnings("deprecation")
@SysUISingleton
-public class UdfpsController implements DozeReceiver, HbmCallback {
+public class UdfpsController implements DozeReceiver {
private static final String TAG = "UdfpsController";
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
@@ -110,6 +111,7 @@
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
+ @Nullable private final UdfpsHbmCallback mHbmCallback;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -466,7 +468,8 @@
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
@NonNull ScreenLifecycle screenLifecycle,
- @Nullable Vibrator vibrator) {
+ @Nullable Vibrator vibrator,
+ @NonNull Optional<UdfpsHbmCallback> hbmCallback) {
mContext = context;
// TODO (b/185124905): inject main handler and vibrator once done prototyping
mMainHandler = new Handler(Looper.getMainLooper());
@@ -486,6 +489,7 @@
mFalsingManager = falsingManager;
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
+ mHbmCallback = hbmCallback.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
@@ -619,7 +623,7 @@
Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason);
mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
mView.setSensorProperties(mSensorProps);
- mView.setHbmCallback(this);
+ mView.setHbmCallback(mHbmCallback);
UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
animation.init();
mView.setAnimationViewController(animation);
@@ -791,17 +795,6 @@
mView.stopIllumination();
}
- @Override
- public void enableHbm(@HbmType int hbmType, @Nullable Surface surface) {
- // Do nothing. This method can be implemented for devices that require the high-brightness
- // mode for fingerprint illumination.
- }
-
- @Override
- public void disableHbm(@HbmType int hbmType, @Nullable Surface surface) {
- // Do nothing. This method can be implemented for devices that require the high-brightness
- // mode for fingerprint illumination.
- }
private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) {
if (TextUtils.isEmpty(effect)) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
index d90d0f8..85f0d27 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
@@ -19,18 +19,18 @@
import android.annotation.Nullable;
import android.view.Surface;
-import com.android.systemui.biometrics.HbmTypes.HbmType;
+import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
/**
* Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
* enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
* illumination is no longer necessary.
*/
-public interface HbmCallback {
+public interface UdfpsHbmCallback {
/**
* UdfpsView will call this to enable the HBM when the fingerprint illumination is needed.
*
- * @param hbmType The type of HBM that should be enabled. See {@link HbmTypes}.
+ * @param hbmType The type of HBM that should be enabled. See {@link UdfpsHbmTypes}.
* @param surface The surface for which the HBM is requested, in case the HBM implementation
* needs to set special surface flags to enable the HBM. Can be null.
*/
@@ -39,7 +39,7 @@
/**
* UdfpsView will call this to disable the HBM when the illumination is not longer needed.
*
- * @param hbmType The type of HBM that should be disabled. See {@link HbmTypes}.
+ * @param hbmType The type of HBM that should be disabled. See {@link UdfpsHbmTypes}.
* @param surface The surface for which the HBM is requested, in case the HBM implementation
* needs to unset special surface flags to disable the HBM. Can be null.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
index f798005..3ab0bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
@@ -25,7 +25,7 @@
/**
* Different high-brightness mode (HBM) types that are relevant to this package.
*/
-public final class HbmTypes {
+public final class UdfpsHbmTypes {
/** HBM that applies to the whole screen. */
public static final int GLOBAL_HBM = IUdfpsHbmListener.GLOBAL_HBM;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
index 8bea05b..1676bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
@@ -26,7 +26,7 @@
/**
* @param callback Invoked when HBM should be enabled or disabled.
*/
- void setHbmCallback(@Nullable HbmCallback callback);
+ void setHbmCallback(@Nullable UdfpsHbmCallback callback);
/**
* Invoked when illumination should start.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
index 4d441bd..aa5f0f6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -31,7 +31,7 @@
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import com.android.systemui.biometrics.HbmTypes.HbmType;
+import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
/**
* Under-display fingerprint sensor Surface View. The surface should be used for HBM-specific things
@@ -41,7 +41,7 @@
private static final String TAG = "UdfpsSurfaceView";
private static final String SETTING_HBM_TYPE =
"com.android.systemui.biometrics.UdfpsSurfaceView.hbmType";
- private static final @HbmType int DEFAULT_HBM_TYPE = HbmTypes.GLOBAL_HBM;
+ private static final @HbmType int DEFAULT_HBM_TYPE = UdfpsHbmTypes.GLOBAL_HBM;
/**
* This is used instead of {@link android.graphics.drawable.Drawable}, because the latter has
@@ -57,7 +57,7 @@
private final @HbmType int mHbmType;
@NonNull private RectF mSensorRect;
- @Nullable private HbmCallback mHbmCallback;
+ @Nullable private UdfpsHbmCallback mHbmCallback;
public UdfpsSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -90,7 +90,7 @@
}
@Override
- public void setHbmCallback(@Nullable HbmCallback callback) {
+ public void setHbmCallback(@Nullable UdfpsHbmCallback callback) {
mHbmCallback = callback;
}
@@ -102,7 +102,7 @@
Log.e(TAG, "startIllumination | mHbmCallback is null");
}
- if (mHbmType == HbmTypes.GLOBAL_HBM) {
+ if (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) {
drawImmediately(mIlluminationDotDrawable);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index f10d5f3..a1d3040 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -101,7 +101,7 @@
}
@Override
- public void setHbmCallback(@Nullable HbmCallback callback) {
+ public void setHbmCallback(@Nullable UdfpsHbmCallback callback) {
mHbmSurfaceView.setHbmCallback(callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 26be987..a904cef 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -120,7 +120,7 @@
private val onSeedingComplete = Consumer<Boolean> {
accepted ->
if (accepted) {
- selectedStructure = controlsController.get().getFavorites().maxBy {
+ selectedStructure = controlsController.get().getFavorites().maxByOrNull {
it.controls.size
} ?: EMPTY_STRUCTURE
updatePreferences(selectedStructure)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7fa48d4..1396099 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -29,6 +29,7 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
+import com.android.systemui.biometrics.UdfpsHbmCallback;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
@@ -160,6 +161,9 @@
@BindsOptionalOf
abstract StatusBar optionalStatusBar();
+ @BindsOptionalOf
+ abstract UdfpsHbmCallback optionalUdfpsHbmCallback();
+
@SysUISingleton
@Binds
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index a83b13c..3873c25 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -226,7 +226,7 @@
ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
this::getWalletViewController, mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter);
+ mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger());
if (shouldShowLockMessage(dialog)) {
dialog.showLockMessage();
@@ -294,11 +294,11 @@
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter) {
+ MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
adapter, overflowAdapter, depthController, sysuiColorExtractor,
statusBarService, notificationShadeWindowController, sysuiState,
- onRotateCallback, keyguardShowing, powerAdapter);
+ onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger);
mWalletFactory = walletFactory;
// Update window attributes
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 2eb3762..f9bb35fc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -249,7 +249,43 @@
GA_SCREENSHOT_PRESS(347),
@UiEvent(doc = "The global actions screenshot button was long pressed.")
- GA_SCREENSHOT_LONG_PRESS(348);
+ GA_SCREENSHOT_LONG_PRESS(348),
+
+ @UiEvent(doc = "The global actions power off button was pressed.")
+ GA_SHUTDOWN_PRESS(802),
+
+ @UiEvent(doc = "The global actions power off button was long pressed.")
+ GA_SHUTDOWN_LONG_PRESS(803),
+
+ @UiEvent(doc = "The global actions reboot button was pressed.")
+ GA_REBOOT_PRESS(349),
+
+ @UiEvent(doc = "The global actions reboot button was long pressed.")
+ GA_REBOOT_LONG_PRESS(804),
+
+ @UiEvent(doc = "The global actions lockdown button was pressed.")
+ GA_LOCKDOWN_PRESS(354), // already created by cwren apparently
+
+ @UiEvent(doc = "Power menu was opened via quick settings button.")
+ GA_OPEN_QS(805),
+
+ @UiEvent(doc = "Power menu was opened via power + volume up.")
+ GA_OPEN_POWER_VOLUP(806),
+
+ @UiEvent(doc = "Power menu was opened via long press on power.")
+ GA_OPEN_LONG_PRESS_POWER(807),
+
+ @UiEvent(doc = "Power menu was closed via long press on power.")
+ GA_CLOSE_LONG_PRESS_POWER(808),
+
+ @UiEvent(doc = "Power menu was dismissed by back gesture.")
+ GA_CLOSE_BACK(809),
+
+ @UiEvent(doc = "Power menu was dismissed by tapping outside dialog.")
+ GA_CLOSE_TAP_OUTSIDE(810),
+
+ @UiEvent(doc = "Power menu was closed via power + volume up.")
+ GA_CLOSE_POWER_VOLUP(811);
private final int mId;
@@ -349,6 +385,10 @@
return mContext;
}
+ protected UiEventLogger getEventLogger() {
+ return mUiEventLogger;
+ }
+
/**
* Show the global actions dialog (creating if necessary)
*
@@ -581,7 +621,7 @@
mAdapter, mOverflowAdapter,
mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter);
+ mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -679,13 +719,14 @@
@VisibleForTesting
final class ShutDownAction extends SinglePressAction implements LongPressAction {
- private ShutDownAction() {
+ ShutDownAction() {
super(R.drawable.ic_lock_power_off,
R.string.global_action_power_off);
}
@Override
public boolean onLongPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.reboot(true);
return true;
@@ -705,6 +746,7 @@
@Override
public void onPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_PRESS);
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown();
}
@@ -807,12 +849,13 @@
@VisibleForTesting
final class RestartAction extends SinglePressAction implements LongPressAction {
- private RestartAction() {
+ RestartAction() {
super(R.drawable.ic_restart, R.string.global_action_restart);
}
@Override
public boolean onLongPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.reboot(true);
return true;
@@ -832,6 +875,7 @@
@Override
public void onPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_PRESS);
mWindowManagerFuncs.reboot(false);
}
}
@@ -1062,6 +1106,7 @@
public void onPress() {
mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
UserHandle.USER_ALL);
+ mUiEventLogger.log(GlobalActionsEvent.GA_LOCKDOWN_PRESS);
try {
mIWindowManager.lockNow(null);
// Lock profiles (if any) on the background thread.
@@ -2049,6 +2094,7 @@
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRotateCallback;
+ private UiEventLogger mUiEventLogger;
protected ViewGroup mContainer;
@@ -2058,7 +2104,7 @@
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter) {
+ MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
super(context, themeRes);
mContext = context;
mAdapter = adapter;
@@ -2071,6 +2117,7 @@
mSysUiState = sysuiState;
mOnRotateCallback = onRotateCallback;
mKeyguardShowing = keyguardShowing;
+ mUiEventLogger = uiEventLogger;
// Window initialization
Window window = getWindow();
@@ -2141,7 +2188,7 @@
mGlobalActionsLayout.setAdapter(mAdapter);
mContainer = findViewById(com.android.systemui.R.id.global_actions_container);
mContainer.setOnClickListener(v -> {
- // TODO: add logging (b/182830510)
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
cancel();
});
@@ -2218,6 +2265,12 @@
}
@Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_BACK);
+ }
+
+ @Override
public void show() {
super.show();
// split this up so we can override but still call Dialog.show
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 644876c..ef53233 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -115,7 +115,7 @@
private var needsReordering: Boolean = false
private var keysNeedRemoval = mutableSetOf<String>()
private var bgColor = getBackgroundColor()
- private var shouldScrollToActivePlayer: Boolean = false
+ protected var shouldScrollToActivePlayer: Boolean = false
private var isRtl: Boolean = false
set(value) {
if (value != field) {
@@ -184,7 +184,14 @@
true /* persistent */)
mediaManager.addListener(object : MediaDataManager.Listener {
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- addOrUpdatePlayer(key, oldKey, data)
+ if (addOrUpdatePlayer(key, oldKey, data)) {
+ MediaPlayerData.getMediaPlayer(key, null)?.let {
+ logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
+ it.mInstanceId,
+ /* isRecommendationCard */ false,
+ it.surfaceForSmartspaceLogging)
+ }
+ }
val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
if (canRemove && !Utils.useMediaResumption(context)) {
// This view isn't playing, let's remove this! This happens e.g when
@@ -203,6 +210,12 @@
override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
Log.d(TAG, "My Smartspace media update is here")
addSmartspaceMediaRecommendations(key, data)
+ MediaPlayerData.getMediaPlayer(key, null)?.let {
+ logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
+ it.mInstanceId,
+ /* isRecommendationCard */ true,
+ it.surfaceForSmartspaceLogging)
+ }
if (mediaCarouselScrollHandler.visibleToUser) {
logSmartspaceImpression()
}
@@ -276,7 +289,8 @@
}
}
- private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) {
+ // Returns true if new player is added
+ private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData): Boolean {
val dataCopy = data.copy(backgroundColor = bgColor)
val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey)
if (existingPlayer == null) {
@@ -295,7 +309,7 @@
} else {
existingPlayer.bindPlayer(dataCopy, key)
MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer)
- if (visualStabilityManager.isReorderingAllowed) {
+ if (visualStabilityManager.isReorderingAllowed || shouldScrollToActivePlayer) {
reorderAllPlayers()
} else {
needsReordering = true
@@ -309,6 +323,7 @@
if (MediaPlayerData.players().size != mediaContent.childCount) {
Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync")
}
+ return existingPlayer == null
}
private fun addSmartspaceMediaRecommendations(key: String, data: SmartspaceTarget) {
@@ -325,7 +340,7 @@
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp)
- newRecs.bindRecommendation(data, bgColor, { v -> shouldScrollToActivePlayer = true })
+ newRecs.bindRecommendation(data, bgColor)
MediaPlayerData.addMediaRecommendation(key, newRecs)
updatePlayerToState(newRecs, noAnimation = true)
reorderAllPlayers()
@@ -520,12 +535,20 @@
this.desiredLocation = desiredLocation
this.desiredHostState = it
currentlyExpanded = it.expansion > 0
+
+ val shouldCloseGuts = !currentlyExpanded && !mediaManager.hasActiveMedia() &&
+ desiredHostState.showsOnlyActiveMedia
+
for (mediaPlayer in MediaPlayerData.players()) {
if (animate) {
mediaPlayer.mediaViewController.animatePendingStateChange(
duration = duration,
delay = startDelay)
}
+ if (shouldCloseGuts && mediaPlayer.mediaViewController.isGutsVisible) {
+ mediaPlayer.closeGuts(!animate)
+ }
+
mediaPlayer.mediaViewController.onLocationPreChange(desiredLocation)
}
mediaCarouselScrollHandler.showsSettingsButton = !it.showsOnlyActiveMedia
@@ -541,9 +564,9 @@
}
}
- fun closeGuts() {
+ fun closeGuts(immediate: Boolean = true) {
MediaPlayerData.players().forEach {
- it.closeGuts(true)
+ it.closeGuts(immediate)
}
}
@@ -641,7 +664,7 @@
@VisibleForTesting
internal object MediaPlayerData {
private val EMPTY = MediaData(-1, false, 0, null, null, null, null, null,
- emptyList(), emptyList(), "INVALID", null, null, null, false, null)
+ emptyList(), emptyList(), "INVALID", null, null, null, true, null)
data class MediaSortKey(
// Is Smartspace media recommendation. When the Smartspace media is present, it should
@@ -694,7 +717,7 @@
/** Returns the index of the first non-timeout media. */
fun getActiveMediaIndex(): Int {
mediaPlayers.entries.forEachIndexed { index, e ->
- if (e.key.data.active) {
+ if (!e.key.isSsMediaRec && e.key.data.active) {
return index
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index e5a6271..c806bcf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -59,7 +59,7 @@
private val mainExecutor: DelayableExecutor,
private val dismissCallback: () -> Unit,
private var translationChangedListener: () -> Unit,
- private val closeGuts: () -> Unit,
+ private val closeGuts: (immediate: Boolean) -> Unit,
private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
private val logSmartspaceImpression: () -> Unit
@@ -473,7 +473,7 @@
if (oldIndex != visibleMediaIndex && visibleToUser) {
logSmartspaceImpression()
}
- closeGuts()
+ closeGuts(false)
updatePlayerVisibilities()
}
val relativeLocation = visibleMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index fe3463f..3e9559b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -25,7 +25,8 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -212,7 +213,8 @@
mMediaViewController.openGuts();
return true;
} else {
- return false;
+ closeGuts();
+ return true;
}
});
mPlayerViewHolder.getCancel().setOnClickListener(v -> {
@@ -276,7 +278,7 @@
if (mMediaViewController.isGutsVisible()) return;
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- false);
+ /* isRecommendationCard */ false);
mActivityStarter.postStartActivityDismissingKeyguard(clickIntent,
buildLaunchAnimatorController(mPlayerViewHolder.getPlayer()));
});
@@ -384,7 +386,7 @@
button.setEnabled(true);
button.setOnClickListener(v -> {
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- false);
+ /* isRecommendationCard */ false);
action.run();
});
}
@@ -418,7 +420,7 @@
mPlayerViewHolder.getDismiss().setEnabled(isDismissible);
mPlayerViewHolder.getDismiss().setOnClickListener(v -> {
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
- false);
+ /* isRecommendationCard */ false);
if (mKey != null) {
closeGuts();
@@ -472,10 +474,7 @@
}
/** Bind this recommendation view based on the data given. */
- public void bindRecommendation(
- @NonNull SmartspaceTarget target,
- @NonNull int backgroundColor,
- @Nullable View.OnClickListener callback) {
+ public void bindRecommendation(@NonNull SmartspaceTarget target, @NonNull int backgroundColor) {
if (mRecommendationViewHolder == null) {
return;
}
@@ -515,8 +514,9 @@
// Get the logo from app's package name when applicable.
String packageName = extras.getString(EXTRAS_MEDIA_SOURCE_PACKAGE_NAME);
try {
- icon = mContext.getPackageManager().getApplicationIcon(
+ Drawable drawable = mContext.getPackageManager().getApplicationIcon(
packageName);
+ icon = convertToGrayscale(drawable);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "No media source icon can be fetched via package name", e);
}
@@ -528,18 +528,13 @@
// Set up media source app's logo.
ImageView mediaSourceLogoImageView = mediaLogoItems.get(uiComponentIndex);
mediaSourceLogoImageView.setImageDrawable(icon);
- // TODO(b/186699032): Tint the app logo using the accent color.
- mediaSourceLogoImageView.setColorFilter(backgroundColor, PorterDuff.Mode.XOR);
// Set up media item cover.
ImageView mediaCoverImageView = mediaCoverItems.get(uiComponentIndex);
mediaCoverImageView.setImageIcon(recommendation.getIcon());
// Set up the click listener if applicable.
- setSmartspaceRecItemOnClickListener(
- mediaCoverImageView,
- recommendation,
- callback);
+ setSmartspaceRecItemOnClickListener(mediaCoverImageView, recommendation);
if (uiComponentIndex < MEDIA_RECOMMENDATION_ITEMS_PER_ROW) {
setVisibleAndAlpha(collapsedSet,
@@ -563,7 +558,7 @@
// Set up long press to show guts setting panel.
mRecommendationViewHolder.getDismiss().setOnClickListener(v -> {
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
- true);
+ /* isRecommendationCard */ true);
closeGuts();
mKeyguardDismissUtil.executeWhenUnlocked(() -> {
mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
@@ -651,6 +646,15 @@
return (state.getState() == PlaybackState.STATE_PLAYING);
}
+ /** Convert the pass-in source drawable to a grayscale one. */
+ private Drawable convertToGrayscale(Drawable drawable) {
+ ColorMatrix matrix = new ColorMatrix();
+ matrix.setSaturation(0);
+ ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
+ drawable.setColorFilter(filter);
+ return drawable;
+ }
+
private void setVisibleAndAlpha(ConstraintSet set, int actionId, boolean visible) {
set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : ConstraintSet.GONE);
set.setAlpha(actionId, visible ? 1.0f : 0.0f);
@@ -658,8 +662,7 @@
private void setSmartspaceRecItemOnClickListener(
@NonNull View view,
- @NonNull SmartspaceAction action,
- @Nullable View.OnClickListener callback) {
+ @NonNull SmartspaceAction action) {
if (view == null || action == null || action.getIntent() == null) {
Log.e(TAG, "No tap action can be set up");
return;
@@ -668,7 +671,7 @@
view.setOnClickListener(v -> {
// When media recommendation card is shown, it will always be the top card.
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- true);
+ /* isRecommendationCard */ true);
if (shouldSmartspaceRecItemOpenInForeground(action)) {
// Request to unlock the device if the activity needs to be opened in foreground.
@@ -682,9 +685,8 @@
view.getContext().startActivity(action.getIntent());
}
- if (callback != null) {
- callback.onClick(v);
- }
+ // Automatically scroll to the active player once the media is loaded.
+ mMediaCarouselController.setShouldScrollToActivePlayer(true);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 3c28f6e..60e832a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -273,6 +273,7 @@
} else {
updateDesiredLocation()
qsExpanded = false
+ closeGuts()
}
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
index 2d0d5cd..40f908b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
@@ -16,7 +16,6 @@
package com.android.systemui.navigationbar;
-import android.annotation.ColorInt;
import android.content.Context;
import android.view.View;
@@ -46,10 +45,9 @@
}
/**
- * Initialize the controller with visibility change callback and light/dark icon color.
+ * Initialize the controller with visibility change callback.
*/
- public void init(Consumer<Boolean> visibilityChangeCallback, @ColorInt int lightIconColor,
- @ColorInt int darkIconColor) {}
+ public void init(Consumer<Boolean> visibilityChangeCallback) {}
/**
* Set whether the view can be shown.
@@ -72,11 +70,6 @@
public void unregisterListeners() {}
/**
- * Set the dark intensity for all drawables.
- */
- public void setDarkIntensity(float darkIntensity) {}
-
- /**
* Return the current view.
*/
public View getCurrentView() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index fbc7c92..b4f8c10 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -198,7 +198,6 @@
}
mView.getRotationButtonController().setDarkIntensity(darkIntensity);
- Dependency.get(NavigationBarOverlayController.class).setDarkIntensity(darkIntensity);
for (DarkIntensityListener listener : mDarkIntensityListeners) {
listener.onDarkIntensity(darkIntensity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index f82d265d..fcbd596 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -61,7 +61,6 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
@@ -326,8 +325,7 @@
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
- mNavBarOverlayController.init(
- mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor);
+ mNavBarOverlayController.init(mNavbarOverlayVisibilityChangeCallback);
}
mConfiguration = new Configuration();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
index 0f66456..b55d86e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
@@ -18,7 +18,9 @@
import android.content.ContentProvider;
import android.content.ContentValues;
+import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
@@ -27,16 +29,19 @@
import android.util.Log;
import android.widget.RemoteViews;
+import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.shared.system.PeopleProviderUtils;
import javax.inject.Inject;
/** API that returns a People Tile preview. */
-public class PeopleProvider extends ContentProvider {
+public class PeopleProvider extends ContentProvider implements
+ SystemUIAppComponentFactory.ContextInitializer {
private static final String TAG = "PeopleProvider";
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
private static final String EMPTY_STRING = "";
+ private SystemUIAppComponentFactory.ContextAvailableCallback mCallback;
@Inject
PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
@@ -82,8 +87,8 @@
Log.e(TAG, "Could not initialize people widget manager");
return null;
}
- RemoteViews view =
- mPeopleSpaceWidgetManager.getPreview(shortcutId, userHandle, packageName, extras);
+ RemoteViews view = mPeopleSpaceWidgetManager.getPreview(shortcutId, userHandle, packageName,
+ extras);
if (view == null) {
if (DEBUG) Log.d(TAG, "No preview available for shortcutId: " + shortcutId);
return null;
@@ -130,5 +135,17 @@
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new IllegalArgumentException("Invalid method");
}
+
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ mCallback.onContextAvailable(context);
+ super.attachInfo(context, info);
+ }
+
+ @Override
+ public void setContextAvailableCallback(
+ SystemUIAppComponentFactory.ContextAvailableCallback callback) {
+ mCallback = callback;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 29685a4..7b5ab0d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -416,9 +416,8 @@
&& birthdayString == null;
boolean addBirthdayStatus = !hasBirthdayStatus(storedTile, context)
&& birthdayString != null;
- boolean shouldUpdate =
- storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus
- || addBirthdayStatus;
+ boolean shouldUpdate = storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus
+ || addBirthdayStatus;
if (shouldUpdate) {
if (DEBUG) Log.d(TAG, "Update " + storedTile.getUserName() + " from contacts");
manager.updateAppWidgetOptionsAndView(appWidgetId,
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 06f8a60..9fc9cad 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -41,8 +41,6 @@
import android.app.people.PeopleSpaceTile;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
@@ -57,13 +55,13 @@
import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.Pair;
+import android.view.Gravity;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -195,16 +193,10 @@
*/
private RemoteViews getViewForTile() {
if (DEBUG) Log.d(TAG, "Creating view for tile key: " + mKey.toString());
- if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()) {
- if (DEBUG) Log.d(TAG, "Create empty view: " + mTile);
- return createEmptyView();
- }
-
- boolean dndBlockingTileData = isDndBlockingTileData(mTile);
- if (dndBlockingTileData) {
- if (DEBUG) Log.d(TAG, "Create DND view: " + mTile.getNotificationPolicyState());
- // TODO: Create DND view.
- return createEmptyView();
+ if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()
+ || isDndBlockingTileData(mTile)) {
+ if (DEBUG) Log.d(TAG, "Create suppressed view: " + mTile);
+ return createSuppressedView();
}
if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
@@ -265,34 +257,27 @@
return !tile.canBypassDnd();
}
- private RemoteViews createEmptyView() {
- RemoteViews views = new RemoteViews(mContext.getPackageName(),
- R.layout.people_tile_empty_layout);
- Drawable appIcon = getAppBadge(mKey.getPackageName(), mKey.getUserId());
+ private RemoteViews createSuppressedView() {
+ RemoteViews views;
+ if (mTile.isUserQuieted()) {
+ views = new RemoteViews(mContext.getPackageName(),
+ R.layout.people_tile_work_profile_quiet_layout);
+ } else {
+ views = new RemoteViews(mContext.getPackageName(),
+ R.layout.people_tile_suppressed_layout);
+ }
+ Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon);
Bitmap appIconAsBitmap = convertDrawableToBitmap(appIcon);
- FastBitmapDrawable drawable = new FastBitmapDrawable(
- appIconAsBitmap);
+ FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap);
drawable.setIsDisabled(true);
Bitmap convertedBitmap = convertDrawableToBitmap(drawable);
- views.setImageViewBitmap(R.id.item, convertedBitmap);
+ views.setImageViewBitmap(R.id.icon, convertedBitmap);
return views;
}
- private Drawable getAppBadge(String packageName, int userId) {
- Drawable badge = null;
- try {
- final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
- packageName, PackageManager.GET_META_DATA, userId);
- badge = Utils.getBadgedIcon(mContext, appInfo);
- } catch (PackageManager.NameNotFoundException e) {
- badge = mContext.getPackageManager().getDefaultActivityIcon();
- }
- return badge;
- }
-
private void setMaxLines(RemoteViews views, boolean showSender) {
int textSize = mLayoutSize == LAYOUT_LARGE ? getSizeInDp(
- R.dimen.content_text_size_for_medium)
+ R.dimen.content_text_size_for_large)
: getSizeInDp(R.dimen.content_text_size_for_medium);
int lineHeight = getLineHeight(textSize);
int notificationContentHeight = getContentHeightForLayout(lineHeight);
@@ -422,9 +407,6 @@
views.setViewVisibility(R.id.availability, View.GONE);
}
- if (mTile.getUserName() != null) {
- views.setTextViewText(R.id.name, mTile.getUserName().toString());
- }
views.setBoolean(R.id.image, "setClipToOutline", true);
views.setImageViewBitmap(R.id.person_icon,
getPersonIconBitmap(mContext, mTile, maxAvatarSize));
@@ -537,25 +519,31 @@
statusText = getStatusTextByType(status.getActivity());
}
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
- views.setViewVisibility(R.id.messages_count, View.GONE);
- setMaxLines(views, false);
- // Secondary text color for statuses.
- views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorSecondary);
views.setTextViewText(R.id.text_content, statusText);
+ if (mLayoutSize == LAYOUT_LARGE) {
+ views.setInt(R.id.content, "setGravity", Gravity.BOTTOM);
+ }
Icon statusIcon = status.getIcon();
if (statusIcon != null) {
- // No multi-line text with status images on medium layout.
- views.setViewVisibility(R.id.text_content, View.GONE);
+ // No text content styled text on medium or large.
+ views.setViewVisibility(R.id.scrim_layout, View.VISIBLE);
+ views.setImageViewIcon(R.id.status_icon, statusIcon);
// Show 1-line subtext on large layout with status images.
if (mLayoutSize == LAYOUT_LARGE) {
- views.setViewVisibility(R.id.subtext, View.VISIBLE);
- views.setTextViewText(R.id.subtext, statusText);
+ if (DEBUG) Log.d(TAG, "Remove name for large");
+ views.setViewVisibility(R.id.name, View.GONE);
+ views.setColorAttr(R.id.text_content, "setTextColor",
+ android.R.attr.textColorPrimary);
+ } else if (mLayoutSize == LAYOUT_MEDIUM) {
+ views.setViewVisibility(R.id.text_content, View.GONE);
+ views.setTextViewText(R.id.name, statusText);
}
- views.setViewVisibility(R.id.image, View.VISIBLE);
- views.setImageViewIcon(R.id.image, statusIcon);
} else {
- views.setViewVisibility(R.id.image, View.GONE);
+ // Secondary text color for statuses without icons.
+ views.setColorAttr(R.id.text_content, "setTextColor",
+ android.R.attr.textColorSecondary);
+ setMaxLines(views, false);
}
// TODO: Set status pre-defined icons
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
@@ -741,16 +729,23 @@
views.setViewVisibility(R.id.name, View.VISIBLE);
views.setViewVisibility(R.id.text_content, View.VISIBLE);
views.setViewVisibility(R.id.subtext, View.GONE);
+ views.setViewVisibility(R.id.image, View.GONE);
+ views.setViewVisibility(R.id.scrim_layout, View.GONE);
}
if (mLayoutSize == LAYOUT_MEDIUM) {
if (DEBUG) Log.d(TAG, "Set vertical padding: " + mMediumVerticalPadding);
int horizontalPadding = (int) Math.floor(MAX_MEDIUM_PADDING * mDensity);
int verticalPadding = (int) Math.floor(mMediumVerticalPadding * mDensity);
- views.setViewPadding(R.id.item, horizontalPadding, verticalPadding, horizontalPadding,
+ views.setViewPadding(R.id.content, horizontalPadding, verticalPadding,
+ horizontalPadding,
verticalPadding);
}
views.setViewVisibility(R.id.messages_count, View.GONE);
+ if (mTile.getUserName() != null) {
+ views.setTextViewText(R.id.name, mTile.getUserName());
+ }
+
return views;
}
@@ -761,6 +756,9 @@
views.setViewVisibility(R.id.predefined_icon, View.GONE);
views.setViewVisibility(R.id.messages_count, View.GONE);
}
+ if (mTile.getUserName() != null) {
+ views.setTextViewText(R.id.name, mTile.getUserName());
+ }
String status = getLastInteractionString(mContext,
mTile.getLastInteractionTimestamp());
if (status != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index dd1a4af..c459963 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -196,13 +196,10 @@
void updateResources(QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController) {
- LayoutParams layoutParams = (LayoutParams) mQSPanelContainer.getLayoutParams();
- layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height);
- mQSPanelContainer.setLayoutParams(layoutParams);
mQSPanelContainer.setPaddingRelative(
mQSPanelContainer.getPaddingStart(),
- mQSPanelContainer.getPaddingTop(),
+ mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.quick_qs_offset_height),
mQSPanelContainer.getPaddingEnd(),
mContext.getResources().getDimensionPixelSize(R.dimen.qs_container_bottom_padding)
);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 1fa9260..f6d9389 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -29,6 +29,7 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
@@ -73,6 +74,7 @@
private final View mPowerMenuLite;
private final boolean mShowPMLiteButton;
private GlobalActionsDialogLite mGlobalActionsDialog;
+ private final UiEventLogger mUiEventLogger;
private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
new UserInfoController.OnUserInfoChangedListener() {
@@ -122,6 +124,7 @@
startSettingsActivity();
}
} else if (v == mPowerMenuLite) {
+ mUiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
mGlobalActionsDialog.showOrHideDialog(false, true);
}
}
@@ -139,7 +142,7 @@
QuickQSPanelController quickQSPanelController,
TunerService tunerService, MetricsLogger metricsLogger, FalsingManager falsingManager,
@Named(PM_LITE_ENABLED) boolean showPMLiteButton,
- GlobalActionsDialogLite globalActionsDialog) {
+ GlobalActionsDialogLite globalActionsDialog, UiEventLogger uiEventLogger) {
super(view);
mUserManager = userManager;
mUserInfoController = userInfoController;
@@ -161,6 +164,7 @@
mPowerMenuLite = mView.findViewById(R.id.pm_lite);
mShowPMLiteButton = showPMLiteButton;
mGlobalActionsDialog = globalActionsDialog;
+ mUiEventLogger = uiEventLogger;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 73a6b34..26332f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -23,7 +23,6 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.MathUtils;
import android.util.Pair;
import android.view.DisplayCutout;
import android.view.View;
@@ -36,9 +35,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.qs.QSDetail.Callback;
-import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.StatusIconContainer;
@@ -67,7 +64,11 @@
private View mQSCarriers;
private Clock mClockView;
- private Space mSpace;
+ private Space mDatePrivacySeparator;
+ private View mClockIconsSeparator;
+ private boolean mShowClockIconsSeparator;
+ private ViewGroup mRightLayout;
+
private BatteryMeterView mBatteryRemainingIcon;
private StatusIconContainer mIconContainer;
private View mPrivacyChip;
@@ -80,7 +81,7 @@
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
- private float mClockIconsAlpha = 1.0f;
+ private float mViewAlpha = 1.0f;
private float mKeyguardExpansionFraction;
private int mTextColorPrimary = Color.TRANSPARENT;
private int mTopViewMeasureHeight;
@@ -117,9 +118,11 @@
mPrivacyChip = findViewById(R.id.privacy_chip);
mDateView = findViewById(R.id.date);
mSecurityHeaderView = findViewById(R.id.header_text_container);
+ mClockIconsSeparator = findViewById(R.id.separator);
+ mRightLayout = findViewById(R.id.rightLayout);
mClockView = findViewById(R.id.clock);
- mSpace = findViewById(R.id.space);
+ mDatePrivacySeparator = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
@@ -230,36 +233,36 @@
}
private void updateAlphaAnimator() {
- StatusBarIconView noCallingIcon =
- ((StatusBarIconView) mIconContainer.getViewForSlot(mMobileSlotName));
- StatusBarIconView callStrengthIcon =
- ((StatusBarIconView) mIconContainer.getViewForSlot(mCallStrengthSlotName));
TouchAnimator.Builder builder = new TouchAnimator.Builder()
// The following two views have to be hidden manually, so as not to hide the
// Privacy chip in QQS
.addFloat(mDateView, "alpha", 0, 1)
.addFloat(mSecurityHeaderView, "alpha", 0, 1)
- .addFloat(mQSCarriers, "alpha", 0, 1);
- builder.setListener(new TouchAnimator.ListenerAdapter() {
- @Override
- public void onAnimationAtEnd() {
- mIconContainer.addIgnoredSlot(mMobileSlotName);
- mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
- }
+ .addFloat(mQSCarriers, "alpha", 0, 1)
+ .setListener(new TouchAnimator.ListenerAdapter() {
+ @Override
+ public void onAnimationAtEnd() {
+ mIconContainer.addIgnoredSlot(mMobileSlotName);
+ mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
+ }
- @Override
- public void onAnimationStarted() {
- mIconContainer.addIgnoredSlot(mMobileSlotName);
- mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
- }
+ @Override
+ public void onAnimationStarted() {
+ mIconContainer.addIgnoredSlot(mMobileSlotName);
+ mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
- @Override
- public void onAnimationAtStart() {
- super.onAnimationAtStart();
- mIconContainer.removeIgnoredSlot(mMobileSlotName);
- mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
- }
- });
+ setSeparatorVisibility(false);
+ }
+
+ @Override
+ public void onAnimationAtStart() {
+ super.onAnimationAtStart();
+ mIconContainer.removeIgnoredSlot(mMobileSlotName);
+ mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
+
+ setSeparatorVisibility(mShowClockIconsSeparator);
+ }
+ });
mAlphaAnimator = builder.build();
}
@@ -337,20 +340,30 @@
cutout, cornerCutoutPadding, -1);
mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
+ LinearLayout.LayoutParams datePrivacySeparatorLayoutParams =
+ (LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
+ LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
+ (LinearLayout.LayoutParams) mClockIconsSeparator.getLayoutParams();
boolean cornerCutout = cornerCutoutPadding != null
&& (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
if (cutout != null) {
Rect topCutout = cutout.getBoundingRectTop();
if (topCutout.isEmpty() || cornerCutout) {
- lp.width = 0;
- mSpace.setVisibility(View.GONE);
+ datePrivacySeparatorLayoutParams.width = 0;
+ mDatePrivacySeparator.setVisibility(View.GONE);
+ mClockIconsSeparatorLayoutParams.width = 0;
+ setSeparatorVisibility(false);
+ mShowClockIconsSeparator = false;
} else {
- lp.width = topCutout.width();
- mSpace.setVisibility(View.VISIBLE);
+ datePrivacySeparatorLayoutParams.width = topCutout.width();
+ mDatePrivacySeparator.setVisibility(View.VISIBLE);
+ mClockIconsSeparatorLayoutParams.width = topCutout.width();
+ mShowClockIconsSeparator = true;
+ setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
}
}
- mSpace.setLayoutParams(lp);
+ mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
+ mClockIconsSeparator.setLayoutParams(mClockIconsSeparatorLayoutParams);
mCutOutPaddingLeft = padding.first;
mCutOutPaddingRight = padding.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
@@ -358,6 +371,32 @@
return super.onApplyWindowInsets(insets);
}
+ /**
+ * Sets the visibility of the separator between clock and icons.
+ *
+ * This separator is "visible" when there is a center cutout, to block that space. In that
+ * case, the clock and the layout on the right (containing the icons and the battery meter) are
+ * set to weight 1 to take the available space.
+ * @param visible whether the separator between clock and icons should be visible.
+ */
+ private void setSeparatorVisibility(boolean visible) {
+ int newVisibility = visible ? View.VISIBLE : View.GONE;
+ if (mClockIconsSeparator.getVisibility() == newVisibility) return;
+
+ mClockIconsSeparator.setVisibility(visible ? View.VISIBLE : View.GONE);
+ mQSCarriers.setVisibility(visible ? View.GONE : View.VISIBLE);
+
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mClockView.getLayoutParams();
+ lp.width = visible ? 0 : WRAP_CONTENT;
+ lp.weight = visible ? 1f : 0f;
+ mClockView.setLayoutParams(lp);
+
+ lp = (LinearLayout.LayoutParams) mRightLayout.getLayoutParams();
+ lp.width = visible ? 0 : WRAP_CONTENT;
+ lp.weight = visible ? 1f : 0f;
+ mRightLayout.setLayoutParams(lp);
+ }
+
private void updateHeadersPadding() {
setContentMargins(mDatePrivacyView, 0, 0);
setContentMargins(mClockIconsView, 0, 0);
@@ -408,35 +447,12 @@
}
/**
- * When QS is scrolling, mClockIconsAlpha should scroll away and fade out.
- *
- * For a given scroll level, this method does the following:
- * <ol>
- * <li>Determine the alpha that {@code mClockIconsView} should have when the panel is fully
- * expanded.</li>
- * <li>Set the scroll of {@code mClockIconsView} to the same of {@code QSPanel}.</li>
- * <li>Set the alpha of {@code mClockIconsView} to that determined by the expansion of
- * the panel, interpolated between 1 (no expansion) and {@code mClockIconsAlpha} (fully
- * expanded), matching the animator.</li>
- * </ol>
+ * Scroll the headers away.
*
* @param scrollY the scroll of the QSPanel container
*/
public void setExpandedScrollAmount(int scrollY) {
- // The scrolling of the expanded qs has changed. Since the header text isn't part of it,
- // but would overlap content, we're fading it out.
- float newAlpha = 1.0f;
- if (mClockIconsView.getHeight() > 0) {
- newAlpha = MathUtils.map(0, mClockIconsView.getHeight() / 2.0f, 1.0f, 0.0f,
- scrollY);
- newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha);
- }
mClockIconsView.setScrollY(scrollY);
- if (newAlpha != mClockIconsAlpha) {
- mClockIconsAlpha = newAlpha;
- mClockIconsView.setAlpha(MathUtils.lerp(1.0f, mClockIconsAlpha,
- mKeyguardExpansionFraction));
- updateAlphaAnimator();
- }
+ mDatePrivacyView.setScrollY(scrollY);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 2d777a5..b3ec39f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tileimpl
+import android.animation.ArgbEvaluator
+import android.animation.PropertyValuesHolder
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.ColorStateList
@@ -43,6 +45,7 @@
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTile.BooleanState
import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
import java.util.Objects
private const val TAG = "QSTileViewImpl"
@@ -54,6 +57,10 @@
companion object {
private const val INVALID = -1
+ private const val BACKGROUND_NAME = "background"
+ private const val LABEL_NAME = "label"
+ private const val SECONDARY_LABEL_NAME = "secondaryLabel"
+ private const val CHEVRON_NAME = "chevron"
}
override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
@@ -83,9 +90,19 @@
private lateinit var ripple: RippleDrawable
private lateinit var colorBackgroundDrawable: Drawable
private var paintColor: Int = 0
- private var paintAnimator: ValueAnimator? = null
- private var labelAnimator: ValueAnimator? = null
- private var secondaryLabelAnimator: ValueAnimator? = null
+ private val singleAnimator: ValueAnimator = ValueAnimator().apply {
+ setDuration(QS_ANIM_LENGTH)
+ addUpdateListener { animation ->
+ setAllColors(
+ // These casts will throw an exception if some property is missing. We should
+ // always have all properties.
+ animation.getAnimatedValue(BACKGROUND_NAME) as Int,
+ animation.getAnimatedValue(LABEL_NAME) as Int,
+ animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int,
+ animation.getAnimatedValue(CHEVRON_NAME) as Int
+ )
+ }
+ }
private var accessibilityClass: String? = null
private var stateDescriptionDeltas: CharSequence? = null
@@ -104,8 +121,7 @@
clipToPadding = false
isFocusable = true
background = createTileBackground()
- paintColor = getCircleColor(QSTile.State.DEFAULT_STATE)
- colorBackgroundDrawable.setTint(paintColor)
+ setColor(getBackgroundColorForState(QSTile.State.DEFAULT_STATE))
val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
@@ -166,8 +182,8 @@
labelContainer.ignoreLastView = true
secondaryLabel.alpha = 0f
}
- label.setTextColor(getLabelColor(QSTile.State.DEFAULT_STATE))
- secondaryLabel.setTextColor(getSecondaryLabelColor(QSTile.State.DEFAULT_STATE))
+ setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE))
+ setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE))
addView(labelContainer)
}
@@ -176,6 +192,7 @@
.inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup
customDrawableView = sideView.requireViewById(R.id.customDrawable)
chevronView = sideView.requireViewById(R.id.chevron)
+ setChevronColor(getChevronColorForState(QSTile.State.DEFAULT_STATE))
addView(sideView)
}
@@ -322,19 +339,6 @@
icon.setIcon(state, allowAnimations)
contentDescription = state.contentDescription
- // Background color animation
- val newColor = getCircleColor(state.state)
- if (allowAnimations) {
- animateBackground(newColor)
- } else {
- clearBackgroundAnimator()
- colorBackgroundDrawable.setTintList(ColorStateList.valueOf(newColor)).also {
- paintColor = newColor
- }
- paintColor = newColor
- }
- //
-
// State handling and description
val stateDescription = StringBuilder()
val stateText = getStateText(state)
@@ -383,23 +387,80 @@
}
}
- if (allowAnimations) {
- animateLabelColor(getLabelColor(state.state))
- animateSecondaryLabelColor(getSecondaryLabelColor(state.state))
- } else {
- label.setTextColor(getLabelColor(state.state))
- secondaryLabel.setTextColor(getSecondaryLabelColor(state.state))
+ // Colors
+ if (state.state != lastState) {
+ singleAnimator.cancel()
+ if (allowAnimations) {
+ singleAnimator.setValues(
+ colorValuesHolder(
+ BACKGROUND_NAME,
+ paintColor,
+ getBackgroundColorForState(state.state)
+ ),
+ colorValuesHolder(
+ LABEL_NAME,
+ label.currentTextColor,
+ getLabelColorForState(state.state)
+ ),
+ colorValuesHolder(
+ SECONDARY_LABEL_NAME,
+ label.currentTextColor,
+ getSecondaryLabelColorForState(state.state)
+ ),
+ colorValuesHolder(
+ CHEVRON_NAME,
+ chevronView.imageTintList?.defaultColor ?: 0,
+ getChevronColorForState(state.state)
+ )
+ )
+ singleAnimator.start()
+ } else {
+ setAllColors(
+ getBackgroundColorForState(state.state),
+ getLabelColorForState(state.state),
+ getLabelColorForState(state.state),
+ getChevronColorForState(state.state)
+ )
+ }
}
// Right side icon
loadSideViewDrawableIfNecessary(state)
- chevronView.imageTintList = ColorStateList.valueOf(getSecondaryLabelColor(state.state))
label.isEnabled = !state.disabledByPolicy
lastState = state.state
}
+ private fun setAllColors(
+ backgroundColor: Int,
+ labelColor: Int,
+ secondaryLabelColor: Int,
+ chevronColor: Int
+ ) {
+ setColor(backgroundColor)
+ setLabelColor(labelColor)
+ setSecondaryLabelColor(secondaryLabelColor)
+ setChevronColor(chevronColor)
+ }
+
+ private fun setColor(color: Int) {
+ colorBackgroundDrawable.setTint(color)
+ paintColor = color
+ }
+
+ private fun setLabelColor(color: Int) {
+ label.setTextColor(color)
+ }
+
+ private fun setSecondaryLabelColor(color: Int) {
+ secondaryLabel.setTextColor(color)
+ }
+
+ private fun setChevronColor(color: Int) {
+ chevronView.imageTintList = ColorStateList.valueOf(color)
+ }
+
private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
if (state.sideViewCustomDrawable != null) {
customDrawableView.setImageDrawable(state.sideViewCustomDrawable)
@@ -446,63 +507,7 @@
return locInScreen.get(1) >= -height
}
- private fun animateBackground(newBackgroundColor: Int) {
- if (newBackgroundColor != paintColor) {
- clearBackgroundAnimator()
- paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener { animation: ValueAnimator ->
- val c = animation.animatedValue as Int
- colorBackgroundDrawable.setTintList(ColorStateList.valueOf(c)).also {
- paintColor = c
- }
- }
- start()
- }
- }
- }
-
- private fun animateLabelColor(color: Int) {
- val currentColor = label.textColors.defaultColor
- if (currentColor != color) {
- clearLabelAnimator()
- labelAnimator = ValueAnimator.ofArgb(currentColor, color)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener {
- label.setTextColor(it.animatedValue as Int)
- }
- start()
- }
- }
- }
-
- private fun animateSecondaryLabelColor(color: Int) {
- val currentColor = secondaryLabel.textColors.defaultColor
- if (currentColor != color) {
- clearSecondaryLabelAnimator()
- secondaryLabelAnimator = ValueAnimator.ofArgb(currentColor, color)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener {
- secondaryLabel.setTextColor(it.animatedValue as Int)
- }
- start()
- }
- }
- }
-
- private fun clearBackgroundAnimator() {
- paintAnimator?.cancel()?.also { paintAnimator = null }
- }
-
- private fun clearLabelAnimator() {
- labelAnimator?.cancel()?.also { labelAnimator = null }
- }
-
- private fun clearSecondaryLabelAnimator() {
- secondaryLabelAnimator?.cancel()?.also { secondaryLabelAnimator = null }
- }
-
- private fun getCircleColor(state: Int): Int {
+ private fun getBackgroundColorForState(state: Int): Int {
return when (state) {
Tile.STATE_ACTIVE -> colorActive
Tile.STATE_INACTIVE -> colorInactive
@@ -514,7 +519,7 @@
}
}
- private fun getLabelColor(state: Int): Int {
+ private fun getLabelColorForState(state: Int): Int {
return when (state) {
Tile.STATE_ACTIVE -> colorLabelActive
Tile.STATE_INACTIVE -> colorLabelInactive
@@ -526,7 +531,7 @@
}
}
- private fun getSecondaryLabelColor(state: Int): Int {
+ private fun getSecondaryLabelColorForState(state: Int): Int {
return when (state) {
Tile.STATE_ACTIVE -> colorLabelActive
Tile.STATE_INACTIVE, Tile.STATE_UNAVAILABLE -> colorLabelUnavailable
@@ -536,4 +541,12 @@
}
}
}
+
+ private fun getChevronColorForState(state: Int): Int = getSecondaryLabelColorForState(state)
+}
+
+private fun colorValuesHolder(name: String, vararg values: Int): PropertyValuesHolder {
+ return PropertyValuesHolder.ofInt(name, *values).apply {
+ setEvaluator(ArgbEvaluator.getInstance())
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index fa28754..30c9b44 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -22,7 +22,6 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
-import android.graphics.Matrix;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
@@ -35,7 +34,6 @@
import android.util.Log;
import android.view.ScrollCaptureResponse;
import android.view.View;
-import android.view.Window;
import android.widget.ImageView;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -68,8 +66,6 @@
public static final String EXTRA_CAPTURE_RESPONSE = "capture-response";
private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
- private static final boolean USE_SHARED_ELEMENT = false;
-
private final UiEventLogger mUiEventLogger;
private final Executor mUiExecutor;
private final Executor mBackgroundExecutor;
@@ -89,6 +85,7 @@
private ListenableFuture<File> mCacheSaveFuture;
private ListenableFuture<ImageLoader.Result> mCacheLoadFuture;
+ private Bitmap mOutputBitmap;
private LongScreenshot mLongScreenshot;
private boolean mTransitionStarted;
@@ -114,7 +111,7 @@
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
super.onCreate(savedInstanceState);
- getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
+
setContentView(R.layout.long_screenshot);
mPreview = requireViewById(R.id.preview);
@@ -173,7 +170,6 @@
}
}, mUiExecutor);
mCacheLoadFuture = null;
- return;
} else {
LongScreenshot longScreenshot = mLongScreenshotHolder.takeLongScreenshot();
if (longScreenshot != null) {
@@ -189,7 +185,6 @@
Log.d(TAG, "onLongScreenshotReceived(longScreenshot=" + longScreenshot + ")");
mLongScreenshot = longScreenshot;
mPreview.setImageDrawable(mLongScreenshot.getDrawable());
- mTransitionView.setImageDrawable(mLongScreenshot.getDrawable());
updateImageDimensions();
mCropView.setVisibility(View.VISIBLE);
mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
@@ -310,19 +305,15 @@
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- if (USE_SHARED_ELEMENT) {
- updateImageDimensions();
- mTransitionView.setVisibility(View.VISIBLE);
- // TODO: listen for transition completing instead of finishing onStop
- mTransitionStarted = true;
- startActivity(intent,
- ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
- } else {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- startActivityAsUser(intent, UserHandle.CURRENT);
- finishAndRemoveTask();
- }
+ mTransitionView.setImageBitmap(mOutputBitmap);
+ mTransitionView.setVisibility(View.VISIBLE);
+ mTransitionView.setTransitionName(
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
+ // TODO: listen for transition completing instead of finishing onStop
+ mTransitionStarted = true;
+ startActivity(intent,
+ ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
}
private void doShare(Uri uri) {
@@ -368,9 +359,11 @@
return;
}
- Bitmap output = renderBitmap(mPreview.getDrawable(), bounds);
+ updateImageDimensions();
+
+ mOutputBitmap = renderBitmap(drawable, bounds);
ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
- mBackgroundExecutor, UUID.randomUUID(), output, ZonedDateTime.now());
+ mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now());
exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor);
}
@@ -419,7 +412,6 @@
// The image width and height on screen
int imageHeight = previewHeight;
int imageWidth = previewWidth;
- float scale;
if (imageRatio > viewRatio) {
// Image is full width and height is constrained, compute extra padding to inform
// CropView
@@ -428,15 +420,13 @@
mCropView.setExtraPadding(extraPadding + mPreview.getPaddingTop(),
extraPadding + mPreview.getPaddingBottom());
imageTop += (previewHeight - imageHeight) / 2;
- scale = imageHeight / bounds.height();
mCropView.setExtraPadding(extraPadding, extraPadding);
mCropView.setImageWidth(previewWidth);
} else {
imageWidth = (int) (previewWidth * imageRatio / viewRatio);
imageLeft += (previewWidth - imageWidth) / 2;
- scale = imageWidth / (float) bounds.width();
// Image is full height
- mCropView.setExtraPadding(mPreview.getPaddingTop(), mPreview.getPaddingBottom());
+ mCropView.setExtraPadding(mPreview.getPaddingTop(), mPreview.getPaddingBottom());
mCropView.setImageWidth((int) (previewHeight * imageRatio));
}
@@ -449,10 +439,5 @@
params.width = boundaries.width();
params.height = boundaries.height();
mTransitionView.setLayoutParams(params);
-
- Matrix matrix = new Matrix();
- matrix.postScale(scale, scale, 0, 0);
- matrix.postTranslate(-boundaries.left, -boundaries.top);
- mTransitionView.setImageMatrix(matrix);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 7f31fdd..5437ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -18,7 +18,6 @@
import static com.android.systemui.statusbar.RemoteInputController.processForRemoteInput;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -35,6 +34,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
import java.util.List;
@@ -46,6 +46,7 @@
@SuppressLint("OverrideAbstract")
public class NotificationListener extends NotificationListenerWithPlugins {
private static final String TAG = "NotificationListener";
+ private static final boolean DEBUG = StatusBar.DEBUG;
private final Context mContext;
private final NotificationManager mNotificationManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 70b3a7b..ca81a7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -169,7 +169,7 @@
Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
boolean started = RemoteViews.startPendingIntent(view, pendingIntent, options);
- if (started) releaseNotificationIfKeptForRemoteInputHistory(entry.getKey());
+ if (started) releaseNotificationIfKeptForRemoteInputHistory(entry);
return started;
});
}
@@ -608,7 +608,11 @@
* (after unlock, if applicable), and will then wait a short time to allow the app to update the
* notification in response to the action.
*/
- private void releaseNotificationIfKeptForRemoteInputHistory(String key) {
+ private void releaseNotificationIfKeptForRemoteInputHistory(NotificationEntry entry) {
+ if (entry == null) {
+ return;
+ }
+ final String key = entry.getKey();
if (isNotificationKeptForRemoteInputHistory(key)) {
mMainHandler.postDelayed(() -> {
if (isNotificationKeptForRemoteInputHistory(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 2481ed4..5f10e55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -29,13 +29,11 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
import com.android.systemui.statusbar.phone.StatusBarMarginUpdatedListener
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
-import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
-import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
-import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import java.lang.IllegalStateException
import java.util.concurrent.Executor
@@ -50,7 +48,7 @@
* will have its gravity set towards the corner (i.e., top-right corner gets top|right gravity), and
* the contained ImageView will be set to center_vertical and away from the corner horizontally. The
* Views will match the status bar top padding and status bar height so that the dot can appear to
- * reside directly after the status bar system contents (basically to the right of the battery).
+ * reside directly after the status bar system contents (basically after the battery).
*
* NOTE: any operation that modifies views directly must run on the provided executor, because
* these views are owned by ScreenDecorations and it runs in its own thread
@@ -85,21 +83,27 @@
// Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
private var uiExecutor: DelayableExecutor? = null
- private var e: DelayableExecutor? = null
+
+ private val marginListener: StatusBarMarginUpdatedListener =
+ object : StatusBarMarginUpdatedListener {
+ override fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int) {
+ setStatusBarMargins(marginLeft, marginRight)
+ }
+ }
private val views: Sequence<View>
get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
init {
- locationPublisher.addCallback(object : StatusBarMarginUpdatedListener {
- override fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int) {
- setStatusBarMargins(marginLeft, marginRight)
- }
- })
+ locationPublisher.addCallback(marginListener)
stateController.addCallback(object : StatusBarStateController.StateListener {
override fun onExpandedChanged(isExpanded: Boolean) {
- setStatusBarExpanded(isExpanded)
+ updateStatusBarState()
+ }
+
+ override fun onStateChanged(newState: Int) {
+ updateStatusBarState()
}
})
}
@@ -108,6 +112,13 @@
uiExecutor = e
}
+ fun setQsExpanded(expanded: Boolean) {
+ dlog("setQsExpanded $expanded")
+ synchronized(lock) {
+ nextViewState = nextViewState.copy(qsExpanded = expanded)
+ }
+ }
+
@UiThread
fun setNewRotation(rot: Int) {
dlog("updateRotation: $rot")
@@ -125,8 +136,8 @@
val index = newCorner.cornerIndex()
val h = when (rot) {
- ROTATION_NONE, ROTATION_UPSIDE_DOWN -> sbHeightPortrait
- ROTATION_LANDSCAPE, ROTATION_SEASCAPE -> sbHeightLandscape
+ 0, 2 -> sbHeightPortrait
+ 1, 3 -> sbHeightLandscape
else -> 0
}
synchronized(lock) {
@@ -326,15 +337,22 @@
}
}
- /**
- * We won't show the dot when quick settings is showing
- */
- private fun setStatusBarExpanded(expanded: Boolean) {
+ private fun updateStatusBarState() {
synchronized(lock) {
- nextViewState = nextViewState.copy(hideDotForQuickSettings = expanded)
+ nextViewState = nextViewState.copy(shadeExpanded = isShadeInQs())
}
}
+ /**
+ * If we are unlocked with an expanded shade, QS is showing. On keyguard, the shade is always
+ * expanded so we use other signals from the panel view controller to know if QS is expanded
+ */
+ @GuardedBy("lock")
+ private fun isShadeInQs(): Boolean {
+ return (stateController.isExpanded && stateController.state == SHADE) ||
+ (stateController.state == SHADE_LOCKED)
+ }
+
private fun scheduleUpdate() {
dlog("scheduleUpdate: ")
@@ -431,13 +449,20 @@
}
}
+private fun vlog(s: String) {
+ if (DEBUG_VERBOSE) {
+ Log.d(TAG, s)
+ }
+}
+
const val TOP_LEFT = 0
const val TOP_RIGHT = 1
const val BOTTOM_RIGHT = 2
const val BOTTOM_LEFT = 3
private const val DURATION = 160L
private const val TAG = "PrivacyDotViewController"
-private const val DEBUG = false
+private const val DEBUG = true
+private const val DEBUG_VERBOSE = false
private fun Int.toGravity(): Int {
return when (this) {
@@ -460,10 +485,10 @@
}
private data class ViewState(
- // don't @ me with names
val systemPrivacyEventIsActive: Boolean = false,
- val hideDotForQuickSettings: Boolean = false,
- val statusBarExpanded: Boolean = false,
+ val shadeExpanded: Boolean = false,
+ val qsExpanded: Boolean = false,
+
val rotation: Int = 0,
val height: Int = 0,
val marginLeft: Int = 0,
@@ -472,7 +497,7 @@
val designatedCorner: View? = null
) {
fun shouldShowDot(): Boolean {
- return systemPrivacyEventIsActive && !hideDotForQuickSettings
+ return systemPrivacyEventIsActive && !shadeExpanded && !qsExpanded
}
fun needsLayout(other: ViewState): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index d6356de..f40f24a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -16,7 +16,9 @@
package com.android.systemui.statusbar.notification.collection.legacy;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
@@ -31,6 +33,7 @@
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.wm.shell.bubbles.Bubbles;
@@ -39,10 +42,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.TreeSet;
import javax.inject.Inject;
@@ -58,13 +63,21 @@
public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, StateListener,
GroupMembershipManager, GroupExpansionManager, Dumpable {
- private static final String TAG = "NotificationGroupManager";
+ private static final String TAG = "NotifGroupManager";
+ private static final boolean DEBUG = StatusBar.DEBUG;
+ private static final boolean SPEW = StatusBar.SPEW;
+ /**
+ * The maximum amount of time (in ms) between the posting of notifications that can be
+ * considered part of the same update batch.
+ */
+ private static final long POST_BATCH_MAX_AGE = 5000;
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
private final ArraySet<OnGroupExpansionChangeListener> mExpansionChangeListeners =
new ArraySet<>();
private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
private final Optional<Bubbles> mBubblesOptional;
+ private final EventBuffer mEventBuffer = new EventBuffer();
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
@@ -134,8 +147,14 @@
* When we want to remove an entry from being tracked for grouping
*/
public void onEntryRemoved(NotificationEntry removed) {
+ if (SPEW) {
+ Log.d(TAG, "onEntryRemoved: entry=" + removed);
+ }
onEntryRemovedInternal(removed, removed.getSbn());
- mIsolatedEntries.remove(removed.getKey());
+ StatusBarNotification oldSbn = mIsolatedEntries.remove(removed.getKey());
+ if (oldSbn != null) {
+ updateSuppression(mGroupMap.get(oldSbn.getGroupKey()));
+ }
}
/**
@@ -162,6 +181,9 @@
// the close future. See b/23676310 for reference.
return;
}
+ if (SPEW) {
+ Log.d(TAG, "onEntryRemovedInternal: entry=" + removed + " group=" + group.groupKey);
+ }
if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
group.children.remove(removed.getKey());
} else {
@@ -182,6 +204,9 @@
* Notify the group manager that a new entry was added
*/
public void onEntryAdded(final NotificationEntry added) {
+ if (SPEW) {
+ Log.d(TAG, "onEntryAdded: entry=" + added);
+ }
updateIsolation(added);
onEntryAddedInternal(added);
}
@@ -195,13 +220,16 @@
String groupKey = getGroupKey(sbn);
NotificationGroup group = mGroupMap.get(groupKey);
if (group == null) {
- group = new NotificationGroup();
+ group = new NotificationGroup(groupKey);
mGroupMap.put(groupKey, group);
for (OnGroupChangeListener listener : mGroupChangeListeners) {
listener.onGroupCreated(group, groupKey);
}
}
+ if (SPEW) {
+ Log.d(TAG, "onEntryAddedInternal: entry=" + added + " group=" + group.groupKey);
+ }
if (isGroupChild) {
NotificationEntry existing = group.children.get(added.getKey());
if (existing != null && existing != added) {
@@ -213,9 +241,11 @@
+ " added removed" + added.isRowRemoved(), new Throwable());
}
group.children.put(added.getKey(), added);
+ addToPostBatchHistory(group, added);
updateSuppression(group);
} else {
group.summary = added;
+ addToPostBatchHistory(group, added);
group.expanded = added.areChildrenExpanded();
updateSuppression(group);
if (!group.children.isEmpty()) {
@@ -231,6 +261,27 @@
}
}
+ private void addToPostBatchHistory(NotificationGroup group, @Nullable NotificationEntry entry) {
+ if (entry == null) {
+ return;
+ }
+ boolean didAdd = group.postBatchHistory.add(new PostRecord(entry));
+ if (didAdd) {
+ trimPostBatchHistory(group.postBatchHistory);
+ }
+ }
+
+ /** remove all history that's too old to be in the batch. */
+ private void trimPostBatchHistory(@NonNull TreeSet<PostRecord> postBatchHistory) {
+ if (postBatchHistory.size() <= 1) {
+ return;
+ }
+ long batchStartTime = postBatchHistory.last().postTime - POST_BATCH_MAX_AGE;
+ while (!postBatchHistory.isEmpty() && postBatchHistory.first().postTime < batchStartTime) {
+ postBatchHistory.pollFirst();
+ }
+ }
+
private void onEntryBecomingChild(NotificationEntry entry) {
updateIsolation(entry);
}
@@ -239,6 +290,9 @@
if (group == null) {
return;
}
+ NotificationEntry prevAlertOverride = group.alertOverride;
+ group.alertOverride = getPriorityConversationAlertOverride(group);
+
int childCount = 0;
boolean hasBubbles = false;
for (NotificationEntry entry : group.children.values()) {
@@ -255,18 +309,150 @@
group.suppressed = group.summary != null && !group.expanded
&& (childCount == 1
|| (childCount == 0
- && group.summary.getSbn().getNotification().isGroupSummary()
- && (hasIsolatedChildren(group) || hasBubbles)));
- if (prevSuppressed != group.suppressed) {
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- if (!mIsUpdatingUnchangedGroup) {
- listener.onGroupSuppressionChanged(group, group.suppressed);
- listener.onGroupsChanged();
+ && group.summary.getSbn().getNotification().isGroupSummary()
+ && (hasIsolatedChildren(group) || hasBubbles)));
+
+ boolean alertOverrideChanged = prevAlertOverride != group.alertOverride;
+ boolean suppressionChanged = prevSuppressed != group.suppressed;
+ if (alertOverrideChanged || suppressionChanged) {
+ if (DEBUG && alertOverrideChanged) {
+ Log.d(TAG, "updateSuppression: alertOverride was=" + prevAlertOverride
+ + " now=" + group.alertOverride + " group:\n" + group);
+ }
+ if (DEBUG && suppressionChanged) {
+ Log.d(TAG,
+ "updateSuppression: suppressed changed to " + group.suppressed
+ + " group:\n" + group);
+ }
+ if (!mIsUpdatingUnchangedGroup) {
+ if (alertOverrideChanged) {
+ mEventBuffer.notifyAlertOverrideChanged(group, prevAlertOverride);
+ }
+ if (suppressionChanged) {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupSuppressionChanged(group, group.suppressed);
+ }
+ }
+ mEventBuffer.notifyGroupsChanged();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, group + " did not notify listeners of above change(s)");
}
}
}
}
+ /**
+ * Finds the isolated logical child of this group which is should be alerted instead.
+ *
+ * Notifications from priority conversations are isolated from their groups to make them more
+ * prominent, however apps may post these with a GroupAlertBehavior that has the group receiving
+ * the alert. This would lead to the group alerting even though the conversation that was
+ * updated was not actually a part of that group. This method finds the best priority
+ * conversation in this situation, if there is one, so they can be set as the alertOverride of
+ * the group.
+ *
+ * @param group the group to check
+ * @return the entry which should receive the alert instead of the group, if any.
+ */
+ @Nullable
+ private NotificationEntry getPriorityConversationAlertOverride(NotificationGroup group) {
+ // GOAL: if there is a priority child which wouldn't alert based on its groupAlertBehavior,
+ // but which should be alerting (because priority conversations are isolated), find it.
+ if (group == null || group.summary == null) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary");
+ }
+ return null;
+ }
+ if (isIsolated(group.summary.getKey())) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: isolated group");
+ }
+ return null;
+ }
+
+ // Precondiions:
+ // * Only necessary when all notifications in the group use GROUP_ALERT_SUMMARY
+ // * Only necessary when at least one notification in the group is on a priority channel
+ if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
+ != Notification.GROUP_ALERT_SUMMARY) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: summary != GROUP_ALERT_SUMMARY");
+ }
+ return null;
+ }
+
+ // Get the important children first, copy the keys for the final importance check,
+ // then add the non-isolated children to the map for unified lookup.
+ HashMap<String, NotificationEntry> children = getImportantConversations(group);
+ if (children == null || children.isEmpty()) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations");
+ }
+ return null;
+ }
+ HashSet<String> importantChildKeys = new HashSet<>(children.keySet());
+ children.putAll(group.children);
+
+ // Ensure all children have GROUP_ALERT_SUMMARY
+ for (NotificationEntry child : children.values()) {
+ if (child.getSbn().getNotification().getGroupAlertBehavior()
+ != Notification.GROUP_ALERT_SUMMARY) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: "
+ + "child != GROUP_ALERT_SUMMARY");
+ }
+ return null;
+ }
+ }
+
+ // Create a merged post history from all the children
+ TreeSet<PostRecord> combinedHistory = new TreeSet<>(group.postBatchHistory);
+ for (String importantChildKey : importantChildKeys) {
+ NotificationGroup importantChildGroup = mGroupMap.get(importantChildKey);
+ combinedHistory.addAll(importantChildGroup.postBatchHistory);
+ }
+ trimPostBatchHistory(combinedHistory);
+
+ // This is a streamlined implementation of the following idea:
+ // * From the subset of notifications in the latest 'batch' of updates. A batch is:
+ // * Notifs posted less than POST_BATCH_MAX_AGE before the most recently posted.
+ // * Only including notifs newer than the second-to-last post of any notification.
+ // * Find the newest child in the batch -- the with the largest 'when' value.
+ // * If the newest child is a priority conversation, set that as the override.
+ HashSet<String> batchKeys = new HashSet<>();
+ long newestChildWhen = -1;
+ NotificationEntry newestChild = null;
+ // Iterate backwards through the post history, tracking the child with the smallest sort key
+ for (PostRecord record : combinedHistory.descendingSet()) {
+ if (batchKeys.contains(record.key)) {
+ // Once you see a notification again, the batch has ended
+ break;
+ }
+ batchKeys.add(record.key);
+ NotificationEntry child = children.get(record.key);
+ if (child != null) {
+ long childWhen = child.getSbn().getNotification().when;
+ if (newestChild == null || childWhen > newestChildWhen) {
+ newestChildWhen = childWhen;
+ newestChild = child;
+ }
+ }
+ }
+ if (newestChild != null && importantChildKeys.contains(newestChild.getKey())) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: result=" + newestChild);
+ }
+ return newestChild;
+ }
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: result=null, newestChild="
+ + newestChild);
+ }
+ return null;
+ }
+
private boolean hasIsolatedChildren(NotificationGroup group) {
return getNumberOfIsolatedChildren(group.summary.getSbn().getGroupKey()) != 0;
}
@@ -281,12 +467,33 @@
return count;
}
+ @Nullable
+ private HashMap<String, NotificationEntry> getImportantConversations(NotificationGroup group) {
+ String groupKey = group.summary.getSbn().getGroupKey();
+ HashMap<String, NotificationEntry> result = null;
+ for (StatusBarNotification sbn : mIsolatedEntries.values()) {
+ if (sbn.getGroupKey().equals(groupKey)) {
+ NotificationEntry entry = mGroupMap.get(sbn.getKey()).summary;
+ if (isImportantConversation(entry)) {
+ if (result == null) {
+ result = new HashMap<>();
+ }
+ result.put(sbn.getKey(), entry);
+ }
+ }
+ }
+ return result;
+ }
+
/**
* Update an entry's group information
* @param entry notification entry to update
* @param oldNotification previous notification info before this update
*/
public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) {
+ if (SPEW) {
+ Log.d(TAG, "onEntryUpdated: entry=" + entry);
+ }
onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(),
oldNotification.getNotification().isGroupSummary());
}
@@ -325,7 +532,17 @@
* Whether the given notification is the summary of a group that is being suppressed
*/
public boolean isSummaryOfSuppressedGroup(StatusBarNotification sbn) {
- return isGroupSuppressed(getGroupKey(sbn)) && sbn.getNotification().isGroupSummary();
+ return sbn.getNotification().isGroupSummary() && isGroupSuppressed(getGroupKey(sbn));
+ }
+
+ /**
+ * If the given notification is a summary, get the group for it.
+ */
+ public NotificationGroup getGroupForSummary(StatusBarNotification sbn) {
+ if (sbn.getNotification().isGroupSummary()) {
+ return mGroupMap.get(getGroupKey(sbn));
+ }
+ return null;
}
private boolean isOnlyChild(StatusBarNotification sbn) {
@@ -545,9 +762,7 @@
if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
return false;
}
- int peopleNotificationType =
- mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
- if (peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON) {
+ if (isImportantConversation(entry)) {
return true;
}
if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.getKey())) {
@@ -560,18 +775,25 @@
|| isGroupNotFullyVisible(notificationGroup));
}
+ private boolean isImportantConversation(NotificationEntry entry) {
+ int peopleNotificationType =
+ mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
+ return peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON;
+ }
+
/**
* Isolate a notification from its group so that it visually shows as its own group.
*
* @param entry the notification to isolate
*/
private void isolateNotification(NotificationEntry entry) {
- StatusBarNotification sbn = entry.getSbn();
-
+ if (SPEW) {
+ Log.d(TAG, "isolateNotification: entry=" + entry);
+ }
// We will be isolated now, so lets update the groups
onEntryRemovedInternal(entry, entry.getSbn());
- mIsolatedEntries.put(sbn.getKey(), sbn);
+ mIsolatedEntries.put(entry.getKey(), entry.getSbn());
onEntryAddedInternal(entry);
// We also need to update the suppression of the old group, because this call comes
@@ -588,6 +810,14 @@
* Update the isolation of an entry, splitting it from the group.
*/
public void updateIsolation(NotificationEntry entry) {
+ // We need to buffer a few events because we do isolation changes in 3 steps:
+ // removeInternal, update mIsolatedEntries, addInternal. This means that often the
+ // alertOverride will update on the removal, however processing the event in that case can
+ // cause problems because the mIsolatedEntries map is not in its final state, so the event
+ // listener may be unable to correctly determine the true state of the group. By delaying
+ // the alertOverride change until after the add phase, we can ensure that listeners only
+ // have to handle a consistent state.
+ mEventBuffer.startBuffering();
boolean isIsolated = isIsolated(entry.getSbn().getKey());
if (shouldIsolate(entry)) {
if (!isIsolated) {
@@ -596,6 +826,7 @@
} else if (isIsolated) {
stopIsolatingNotification(entry);
}
+ mEventBuffer.flushAndStopBuffering();
}
/**
@@ -604,15 +835,15 @@
* @param entry the notification to un-isolate
*/
private void stopIsolatingNotification(NotificationEntry entry) {
- StatusBarNotification sbn = entry.getSbn();
- if (isIsolated(sbn.getKey())) {
- // not isolated anymore, we need to update the groups
- onEntryRemovedInternal(entry, entry.getSbn());
- mIsolatedEntries.remove(sbn.getKey());
- onEntryAddedInternal(entry);
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupsChanged();
- }
+ if (SPEW) {
+ Log.d(TAG, "stopIsolatingNotification: entry=" + entry);
+ }
+ // not isolated anymore, we need to update the groups
+ onEntryRemovedInternal(entry, entry.getSbn());
+ mIsolatedEntries.remove(entry.getKey());
+ onEntryAddedInternal(entry);
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupsChanged();
}
}
@@ -648,33 +879,154 @@
}
/**
+ * A record of a notification being posted, containing the time of the post and the key of the
+ * notification entry. These are stored in a TreeSet by the NotificationGroup and used to
+ * calculate a batch of notifications.
+ */
+ public static class PostRecord implements Comparable<PostRecord> {
+ public final long postTime;
+ public final String key;
+
+ /** constructs a record containing the post time and key from the notification entry */
+ public PostRecord(@NonNull NotificationEntry entry) {
+ this.postTime = entry.getSbn().getPostTime();
+ this.key = entry.getKey();
+ }
+
+ @Override
+ public int compareTo(PostRecord o) {
+ int postTimeComparison = Long.compare(this.postTime, o.postTime);
+ return postTimeComparison == 0
+ ? String.CASE_INSENSITIVE_ORDER.compare(this.key, o.key)
+ : postTimeComparison;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PostRecord that = (PostRecord) o;
+ return postTime == that.postTime && key.equals(that.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(postTime, key);
+ }
+ }
+
+ /**
* Represents a notification group in the notification shade.
*/
public static class NotificationGroup {
+ public final String groupKey;
public final HashMap<String, NotificationEntry> children = new HashMap<>();
+ public final TreeSet<PostRecord> postBatchHistory = new TreeSet<>();
public NotificationEntry summary;
public boolean expanded;
/**
* Is this notification group suppressed, i.e its summary is hidden
*/
public boolean suppressed;
+ /**
+ * The child (which is isolated from this group) to which the alert should be transferred,
+ * due to priority conversations.
+ */
+ public NotificationEntry alertOverride;
+
+ NotificationGroup(String groupKey) {
+ this.groupKey = groupKey;
+ }
@Override
public String toString() {
- String result = " summary:\n "
- + (summary != null ? summary.getSbn() : "null")
- + (summary != null && summary.getDebugThrowable() != null
- ? Log.getStackTraceString(summary.getDebugThrowable())
- : "");
- result += "\n children size: " + children.size();
+ StringBuilder sb = new StringBuilder();
+ sb.append(" groupKey: ").append(groupKey);
+ sb.append("\n summary:");
+ appendEntry(sb, summary);
+ sb.append("\n children size: ").append(children.size());
for (NotificationEntry child : children.values()) {
- result += "\n " + child.getSbn()
- + (child.getDebugThrowable() != null
- ? Log.getStackTraceString(child.getDebugThrowable())
- : "");
+ appendEntry(sb, child);
}
- result += "\n summary suppressed: " + suppressed;
- return result;
+ sb.append("\n alertOverride:");
+ appendEntry(sb, alertOverride);
+ sb.append("\n summary suppressed: ").append(suppressed);
+ return sb.toString();
+ }
+
+ private void appendEntry(StringBuilder sb, NotificationEntry entry) {
+ sb.append("\n ").append(entry != null ? entry.getSbn() : "null");
+ if (entry != null && entry.getDebugThrowable() != null) {
+ sb.append(Log.getStackTraceString(entry.getDebugThrowable()));
+ }
+ }
+ }
+
+ /**
+ * This class is a toggleable buffer for a subset of events of {@link OnGroupChangeListener}.
+ * When buffering, instead of notifying the listeners it will set internal state that will allow
+ * it to notify listeners of those events later
+ */
+ private class EventBuffer {
+ private final HashMap<String, NotificationEntry> mOldAlertOverrideByGroup = new HashMap<>();
+ private boolean mIsBuffering = false;
+ private boolean mDidGroupsChange = false;
+
+ void notifyAlertOverrideChanged(NotificationGroup group,
+ NotificationEntry oldAlertOverride) {
+ if (mIsBuffering) {
+ // The value in this map is the override before the event. If there is an entry
+ // already in the map, then we are effectively coalescing two events, which means
+ // we need to preserve the original initial value.
+ mOldAlertOverrideByGroup.putIfAbsent(group.groupKey, oldAlertOverride);
+ } else {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupAlertOverrideChanged(group, oldAlertOverride,
+ group.alertOverride);
+ }
+ }
+ }
+
+ void notifyGroupsChanged() {
+ if (mIsBuffering) {
+ mDidGroupsChange = true;
+ } else {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupsChanged();
+ }
+ }
+ }
+
+ void startBuffering() {
+ mIsBuffering = true;
+ }
+
+ void flushAndStopBuffering() {
+ // stop buffering so that we can call our own helpers
+ mIsBuffering = false;
+ // alert all group alert override changes for groups that were not removed
+ for (Map.Entry<String, NotificationEntry> entry : mOldAlertOverrideByGroup.entrySet()) {
+ NotificationGroup group = mGroupMap.get(entry.getKey());
+ if (group == null) {
+ // The group can be null if this alertOverride changed before the group was
+ // permanently removed, meaning that there's no guarantee that listeners will
+ // that field clear.
+ continue;
+ }
+ NotificationEntry oldAlertOverride = entry.getValue();
+ if (group.alertOverride == oldAlertOverride) {
+ // If the final alertOverride equals the initial, it means we coalesced two
+ // events which undid the change, so we can drop it entirely.
+ continue;
+ }
+ notifyAlertOverrideChanged(group, oldAlertOverride);
+ }
+ mOldAlertOverrideByGroup.clear();
+ // alert that groups changed
+ if (mDidGroupsChange) {
+ notifyGroupsChanged();
+ mDidGroupsChange = false;
+ }
}
}
@@ -714,6 +1066,18 @@
boolean suppressed) {}
/**
+ * The alert override of a group has changed.
+ *
+ * @param group the group that has changed
+ * @param oldAlertOverride the previous notification to which the group's alerts were sent
+ * @param newAlertOverride the notification to which the group's alerts should now be sent
+ */
+ default void onGroupAlertOverrideChanged(
+ NotificationGroup group,
+ @Nullable NotificationEntry oldAlertOverride,
+ @Nullable NotificationEntry newAlertOverride) {}
+
+ /**
* A group of children just received a summary notification and should therefore become
* children of it.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 2b51b56..b9fe9c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -23,6 +23,7 @@
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
@@ -348,7 +349,8 @@
private void showSystemIconArea(boolean animate) {
// Only show the system icon area if we are not currently animating
- if (mAnimationScheduler.getAnimationState() == IDLE) {
+ int state = mAnimationScheduler.getAnimationState();
+ if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
animateShow(mSystemIconArea, animate);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 71ba091..c64b893 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -929,7 +929,7 @@
}
GetWalletCardsRequest request =
new GetWalletCardsRequest(1 /* cardWidth */, 1 /* cardHeight */,
- 1 /* iconSizePx */, 2 /* maxCards */);
+ 1 /* iconSizePx */, 1 /* maxCards */);
mQuickAccessWalletClient.getWalletCards(mUiExecutor, request, mCardRetriever);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 3181f52..9787a944 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -22,12 +22,12 @@
import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.Log;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -41,17 +41,21 @@
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
* A helper class dealing with the alert interactions between {@link NotificationGroupManagerLegacy}
* and {@link HeadsUpManager}. In particular, this class deals with keeping
- * the correct notification in a group alerting based off the group suppression.
+ * the correct notification in a group alerting based off the group suppression and alertOverride.
*/
public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
StateListener {
private static final long ALERT_TRANSFER_TIMEOUT = 300;
+ private static final String TAG = "NotifGroupAlertTransfer";
+ private static final boolean DEBUG = StatusBar.DEBUG;
+ private static final boolean SPEW = StatusBar.SPEW;
/**
* The list of entries containing group alert metadata for each group. Keyed by group key.
@@ -142,41 +146,98 @@
@Override
public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
- if (suppressed) {
- if (mHeadsUpManager.isAlerting(group.summary.getKey())) {
- handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
- }
- } else {
- // Group summary can be null if we are no longer suppressed because the summary was
- // removed. In that case, we don't need to alert the summary.
- if (group.summary == null) {
- return;
- }
- GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
- group.summary.getSbn()));
- // Group is no longer suppressed. We should check if we need to transfer the alert
- // back to the summary now that it's no longer suppressed.
- if (groupAlertEntry.mAlertSummaryOnNextAddition) {
- if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
- alertNotificationWhenPossible(group.summary, mHeadsUpManager);
- }
- groupAlertEntry.mAlertSummaryOnNextAddition = false;
- } else {
- checkShouldTransferBack(groupAlertEntry);
- }
+ if (DEBUG) {
+ Log.d(TAG, "!! onGroupSuppressionChanged: group.summary=" + group.summary
+ + " suppressed=" + suppressed);
}
+ NotificationEntry oldAlertOverride = group.alertOverride;
+ onGroupChanged(group, oldAlertOverride);
+ }
+
+ @Override
+ public void onGroupAlertOverrideChanged(NotificationGroup group,
+ @Nullable NotificationEntry oldAlertOverride,
+ @Nullable NotificationEntry newAlertOverride) {
+ if (DEBUG) {
+ Log.d(TAG, "!! onGroupAlertOverrideChanged: group.summary=" + group.summary
+ + " oldAlertOverride=" + oldAlertOverride
+ + " newAlertOverride=" + newAlertOverride);
+ }
+ onGroupChanged(group, oldAlertOverride);
}
};
- @Override
- public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
+ /**
+ * Called when either the suppressed or alertOverride fields of the group changed
+ *
+ * @param group the group which changed
+ * @param oldAlertOverride the previous value of group.alertOverride
+ */
+ private void onGroupChanged(NotificationGroup group,
+ NotificationEntry oldAlertOverride) {
+ // Group summary can be null if we are no longer suppressed because the summary was
+ // removed. In that case, we don't need to alert the summary.
+ if (group.summary == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onGroupChanged: summary is null");
+ }
+ return;
+ }
+ if (group.suppressed || group.alertOverride != null) {
+ checkForForwardAlertTransfer(group.summary, oldAlertOverride);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "onGroupChanged: maybe transfer back");
+ }
+ GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
+ group.summary.getSbn()));
+ // Group is no longer suppressed or overridden.
+ // We should check if we need to transfer the alert back to the summary.
+ if (groupAlertEntry.mAlertSummaryOnNextAddition) {
+ if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
+ alertNotificationWhenPossible(group.summary);
+ }
+ groupAlertEntry.mAlertSummaryOnNextAddition = false;
+ } else {
+ checkShouldTransferBack(groupAlertEntry);
+ }
+ }
}
- private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting,
- AlertingNotificationManager alertManager) {
- if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.getSbn())) {
- handleSuppressedSummaryAlerted(entry, alertManager);
+ @Override
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+ if (DEBUG) {
+ Log.d(TAG, "!! onHeadsUpStateChanged: entry=" + entry + " isHeadsUp=" + isHeadsUp);
+ }
+ if (isHeadsUp && entry.getSbn().getNotification().isGroupSummary()) {
+ // a group summary is alerting; trigger the forward transfer checks
+ checkForForwardAlertTransfer(entry, /* oldAlertOverride */ null);
+ }
+ }
+
+ /**
+ * Handles changes in a group's suppression or alertOverride, but where at least one of those
+ * conditions is still true (either the group is suppressed, the group has an alertOverride,
+ * or both). The method determined which kind of child needs to receive the alert, finds the
+ * entry currently alerting, and makes the transfer.
+ *
+ * Internally, this is handled with two main cases: the override needs the alert, or there is
+ * no override but the summary is suppressed (so an isolated child needs the alert).
+ *
+ * @param summary the notification entry of the summary of the logical group.
+ * @param oldAlertOverride the former value of group.alertOverride, before whatever event
+ * required us to check for for a transfer condition.
+ */
+ private void checkForForwardAlertTransfer(NotificationEntry summary,
+ NotificationEntry oldAlertOverride) {
+ if (DEBUG) {
+ Log.d(TAG, "checkForForwardAlertTransfer: enter");
+ }
+ NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
+ if (group != null && group.alertOverride != null) {
+ handleOverriddenSummaryAlerted(summary);
+ } else if (mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())) {
+ handleSuppressedSummaryAlerted(summary, oldAlertOverride);
}
}
@@ -186,9 +247,16 @@
// see as early as we can if we need to abort a transfer.
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
+ if (DEBUG) {
+ Log.d(TAG, "!! onPendingEntryAdded: entry=" + entry);
+ }
String groupKey = mGroupManager.getGroupKey(entry.getSbn());
GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
- if (groupAlertEntry != null) {
+ if (groupAlertEntry != null && groupAlertEntry.mGroup.alertOverride == null) {
+ // new pending group entries require us to transfer back from the child to the
+ // group, but alertOverrides are only present in very limited circumstances, so
+ // while it's possible the group should ALSO alert, the previous detection which set
+ // this alertOverride won't be invalidated by this notification added to this group.
checkShouldTransferBack(groupAlertEntry);
}
}
@@ -262,43 +330,128 @@
}
/**
- * Handles the scenario where a summary that has been suppressed is alerted. A suppressed
+ * Handles the scenario where a summary that has been suppressed is itself, or has a former
+ * alertOverride (in the form of an isolated logical child) which was alerted. A suppressed
* summary should for all intents and purposes be invisible to the user and as a result should
* not alert. When this is the case, it is our responsibility to pass the alert to the
* appropriate child which will be the representative notification alerting for the group.
*
- * @param summary the summary that is suppressed and alerting
- * @param alertManager the alert manager that manages the alerting summary
+ * @param summary the summary that is suppressed and (potentially) alerting
+ * @param oldAlertOverride the alertOverride before whatever event triggered this method. If
+ * the alert override was removed, this will be the entry that should
+ * be transferred back from.
*/
private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
- @NonNull AlertingNotificationManager alertManager) {
- StatusBarNotification sbn = summary.getSbn();
+ NotificationEntry oldAlertOverride) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: summary=" + summary);
+ }
GroupAlertEntry groupAlertEntry =
- mGroupAlertEntries.get(mGroupManager.getGroupKey(sbn));
+ mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
+
if (!mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())
- || !alertManager.isAlerting(sbn.getKey())
|| groupAlertEntry == null) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: invalid state");
+ }
+ return;
+ }
+ boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
+ boolean priorityIsAlerting = oldAlertOverride != null
+ && mHeadsUpManager.isAlerting(oldAlertOverride.getKey());
+ if (!summaryIsAlerting && !priorityIsAlerting) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: no summary or override alerting");
+ }
return;
}
if (pendingInflationsWillAddChildren(groupAlertEntry.mGroup)) {
// New children will actually be added to this group, let's not transfer the alert.
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: pending inflations");
+ }
return;
}
NotificationEntry child =
mGroupManager.getLogicalChildren(summary.getSbn()).iterator().next();
- if (child != null) {
- if (child.getRow().keepInParent()
- || child.isRowRemoved()
- || child.isRowDismissed()) {
- // The notification is actually already removed. No need to alert it.
- return;
+ if (summaryIsAlerting) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: transfer summary -> child");
}
- if (!alertManager.isAlerting(child.getKey()) && onlySummaryAlerts(summary)) {
- groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
+ tryTransferAlertState(summary, /*from*/ summary, /*to*/ child, groupAlertEntry);
+ return;
+ }
+ // Summary didn't have the alert, so we're in "transfer back" territory. First, make sure
+ // it's not too late to transfer back, then transfer the alert from the oldAlertOverride to
+ // the isolated child which should receive the alert.
+ if (!canStillTransferBack(groupAlertEntry)) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: transfer from override: too late");
}
- transferAlertState(summary, child, alertManager);
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: transfer override -> child");
+ }
+ tryTransferAlertState(summary, /*from*/ oldAlertOverride, /*to*/ child, groupAlertEntry);
+ }
+
+ /**
+ * Checks for and handles the scenario where the given entry is the summary of a group which
+ * has an alertOverride, and either the summary itself or one of its logical isolated children
+ * is currently alerting (which happens if the summary is suppressed).
+ */
+ private void handleOverriddenSummaryAlerted(NotificationEntry summary) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: summary=" + summary);
+ }
+ GroupAlertEntry groupAlertEntry =
+ mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
+ NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
+ if (group == null || group.alertOverride == null || groupAlertEntry == null) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: invalid state");
+ }
+ return;
+ }
+ boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
+ if (summaryIsAlerting) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: transfer summary -> override");
+ }
+ tryTransferAlertState(summary, /*from*/ summary, group.alertOverride, groupAlertEntry);
+ return;
+ }
+ // Summary didn't have the alert, so we're in "transfer back" territory. First, make sure
+ // it's not too late to transfer back, then remove the alert from any of the logical
+ // children, and if one of them was alerting, we can alert the override.
+ if (!canStillTransferBack(groupAlertEntry)) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: transfer from child: too late");
+ }
+ return;
+ }
+ List<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.getSbn());
+ if (children == null) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: no children");
+ }
+ return;
+ }
+ children.remove(group.alertOverride); // do not release the alert on our desired destination
+ boolean releasedChild = releaseChildAlerts(children);
+ if (releasedChild) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: transfer child -> override");
+ }
+ tryTransferAlertState(summary, /*from*/ null, group.alertOverride, groupAlertEntry);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: no child alert released");
+ }
}
}
@@ -307,14 +460,37 @@
* immediately to have the incorrect one up as short as possible. The second should alert
* when possible.
*
+ * @param summary entry of the summary
* @param fromEntry entry to transfer alert from
* @param toEntry entry to transfer to
- * @param alertManager alert manager for the alert type
*/
- private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry,
- @NonNull AlertingNotificationManager alertManager) {
- alertManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
- alertNotificationWhenPossible(toEntry, alertManager);
+ private void tryTransferAlertState(
+ NotificationEntry summary,
+ NotificationEntry fromEntry,
+ NotificationEntry toEntry,
+ GroupAlertEntry groupAlertEntry) {
+ if (toEntry != null) {
+ if (toEntry.getRow().keepInParent()
+ || toEntry.isRowRemoved()
+ || toEntry.isRowDismissed()) {
+ // The notification is actually already removed. No need to alert it.
+ return;
+ }
+ if (!mHeadsUpManager.isAlerting(toEntry.getKey()) && onlySummaryAlerts(summary)) {
+ groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
+ }
+ if (DEBUG) {
+ Log.d(TAG, "transferAlertState: fromEntry=" + fromEntry + " toEntry=" + toEntry);
+ }
+ transferAlertState(fromEntry, toEntry);
+ }
+ }
+ private void transferAlertState(@Nullable NotificationEntry fromEntry,
+ @NonNull NotificationEntry toEntry) {
+ if (fromEntry != null) {
+ mHeadsUpManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
+ }
+ alertNotificationWhenPossible(toEntry);
}
/**
@@ -326,11 +502,13 @@
* more children are coming. Thus, if a child is added within a certain timeframe after we
* transfer, we back out and alert the summary again.
*
+ * An alert can only transfer back within a small window of time after a transfer away from the
+ * summary to a child happened.
+ *
* @param groupAlertEntry group alert entry to check
*/
private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
- if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
- < ALERT_TRANSFER_TIMEOUT) {
+ if (canStillTransferBack(groupAlertEntry)) {
NotificationEntry summary = groupAlertEntry.mGroup.summary;
if (!onlySummaryAlerts(summary)) {
@@ -338,30 +516,17 @@
}
ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(
summary.getSbn());
- int numChildren = children.size();
+ int numActiveChildren = children.size();
int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
- numChildren += numPendingChildren;
+ int numChildren = numActiveChildren + numPendingChildren;
if (numChildren <= 1) {
return;
}
- boolean releasedChild = false;
- for (int i = 0; i < children.size(); i++) {
- NotificationEntry entry = children.get(i);
- if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
- releasedChild = true;
- mHeadsUpManager.removeNotification(
- entry.getKey(), true /* releaseImmediately */);
- }
- if (mPendingAlerts.containsKey(entry.getKey())) {
- // This is the child that would've been removed if it was inflated.
- releasedChild = true;
- mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
- }
- }
+ boolean releasedChild = releaseChildAlerts(children);
if (releasedChild && !mHeadsUpManager.isAlerting(summary.getKey())) {
- boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
+ boolean notifyImmediately = numActiveChildren > 1;
if (notifyImmediately) {
- alertNotificationWhenPossible(summary, mHeadsUpManager);
+ alertNotificationWhenPossible(summary);
} else {
// Should wait until the pending child inflates before alerting.
groupAlertEntry.mAlertSummaryOnNextAddition = true;
@@ -371,25 +536,61 @@
}
}
+ private boolean canStillTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
+ return SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
+ < ALERT_TRANSFER_TIMEOUT;
+ }
+
+ private boolean releaseChildAlerts(List<NotificationEntry> children) {
+ boolean releasedChild = false;
+ if (SPEW) {
+ Log.d(TAG, "releaseChildAlerts: numChildren=" + children.size());
+ }
+ for (int i = 0; i < children.size(); i++) {
+ NotificationEntry entry = children.get(i);
+ if (SPEW) {
+ Log.d(TAG, "releaseChildAlerts: checking i=" + i + " entry=" + entry
+ + " onlySummaryAlerts=" + onlySummaryAlerts(entry)
+ + " isAlerting=" + mHeadsUpManager.isAlerting(entry.getKey())
+ + " isPendingAlert=" + mPendingAlerts.containsKey(entry.getKey()));
+ }
+ if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
+ releasedChild = true;
+ mHeadsUpManager.removeNotification(
+ entry.getKey(), true /* releaseImmediately */);
+ }
+ if (mPendingAlerts.containsKey(entry.getKey())) {
+ // This is the child that would've been removed if it was inflated.
+ releasedChild = true;
+ mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
+ }
+ }
+ if (SPEW) {
+ Log.d(TAG, "releaseChildAlerts: didRelease=" + releasedChild);
+ }
+ return releasedChild;
+ }
+
/**
* Tries to alert the notification. If its content view is not inflated, we inflate and continue
* when the entry finishes inflating the view.
*
* @param entry entry to show
- * @param alertManager alert manager for the alert type
*/
- private void alertNotificationWhenPossible(@NonNull NotificationEntry entry,
- @NonNull AlertingNotificationManager alertManager) {
- @InflationFlag int contentFlag = alertManager.getContentFlag();
+ private void alertNotificationWhenPossible(@NonNull NotificationEntry entry) {
+ @InflationFlag int contentFlag = mHeadsUpManager.getContentFlag();
final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
if ((params.getContentViews() & contentFlag) == 0) {
+ if (DEBUG) {
+ Log.d(TAG, "alertNotificationWhenPossible: async requestRebind entry=" + entry);
+ }
mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
params.requireContentViews(contentFlag);
mRowContentBindStage.requestRebind(entry, en -> {
PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.getKey());
if (alertInfo != null) {
if (alertInfo.isStillValid()) {
- alertNotificationWhenPossible(entry, mHeadsUpManager);
+ alertNotificationWhenPossible(entry);
} else {
// The transfer is no longer valid. Free the content.
mRowContentBindStage.getStageParams(entry).markContentViewsFreeable(
@@ -400,10 +601,16 @@
});
return;
}
- if (alertManager.isAlerting(entry.getKey())) {
- alertManager.updateNotification(entry.getKey(), true /* alert */);
+ if (mHeadsUpManager.isAlerting(entry.getKey())) {
+ if (DEBUG) {
+ Log.d(TAG, "alertNotificationWhenPossible: continue alerting entry=" + entry);
+ }
+ mHeadsUpManager.updateNotification(entry.getKey(), true /* alert */);
} else {
- alertManager.showNotification(entry);
+ if (DEBUG) {
+ Log.d(TAG, "alertNotificationWhenPossible: start alerting entry=" + entry);
+ }
+ mHeadsUpManager.showNotification(entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index c0d713b..16abd12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -122,6 +122,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -306,6 +307,7 @@
private final QSDetailDisplayer mQSDetailDisplayer;
private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
+ private final PrivacyDotViewController mPrivacyDotViewController;
// Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
// If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
@@ -602,6 +604,7 @@
FeatureFlags featureFlags,
QuickAccessWalletClient quickAccessWalletClient,
KeyguardMediaController keyguardMediaController,
+ PrivacyDotViewController privacyDotViewController,
@Main Executor uiExecutor) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
@@ -610,6 +613,7 @@
mView = view;
mVibratorHelper = vibratorHelper;
mKeyguardMediaController = keyguardMediaController;
+ mPrivacyDotViewController = privacyDotViewController;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
mConfigurationController = configurationController;
@@ -1895,6 +1899,7 @@
mKeyguardBypassController.setQSExpanded(expanded);
mStatusBarKeyguardViewManager.setQsExpanded(expanded);
mLockIconViewController.setQsExpanded(expanded);
+ mPrivacyDotViewController.setQsExpanded(expanded);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 142cf21..ebf2465 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -97,13 +97,13 @@
mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
mIconController = iconController;
+ mCarrierConfigTracker = carrierConfigTracker;
mNetworkController = Dependency.get(NetworkController.class);
mSecurityController = Dependency.get(SecurityController.class);
Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
mNetworkController.addCallback(this);
mSecurityController.addCallback(this);
- mCarrierConfigTracker = carrierConfigTracker;
}
public void destroy() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f45218d..07e9fed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -364,11 +364,11 @@
if (network.equals(mLastNetwork) && validated == lastValidated) {
// Should not rely on getTransportTypes() returning the same order of transport
// types. So sort the array before comparing.
- int[] newTypes = networkCapabilities.getTransportTypes();
+ int[] newTypes = getProcessedTransportTypes(networkCapabilities);
Arrays.sort(newTypes);
int[] lastTypes = (mLastNetworkCapabilities != null)
- ? mLastNetworkCapabilities.getTransportTypes() : null;
+ ? getProcessedTransportTypes(mLastNetworkCapabilities) : null;
if (lastTypes != null) Arrays.sort(lastTypes);
if (Arrays.equals(newTypes, lastTypes)) {
@@ -533,6 +533,21 @@
return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
}
+ private int[] getProcessedTransportTypes(NetworkCapabilities networkCapabilities) {
+ int[] transportTypes = networkCapabilities.getTransportTypes();
+ for (int i = 0; i < transportTypes.length; i++) {
+ // For VCN over WiFi, the transportType is set to be TRANSPORT_CELLULAR in the
+ // NetworkCapabilities, but we need to convert it into TRANSPORT_WIFI in order to
+ // distinguish it from VCN over Cellular.
+ if (transportTypes[i] == NetworkCapabilities.TRANSPORT_CELLULAR
+ && Utils.tryGetWifiInfoForVcn(networkCapabilities) != null) {
+ transportTypes[i] = NetworkCapabilities.TRANSPORT_WIFI;
+ break;
+ }
+ }
+ return transportTypes;
+ }
+
private MobileSignalController getDataController() {
int dataSubId = mSubDefaults.getActiveDataSubId();
return getControllerWithSubId(dataSubId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index edec618..41b1dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -23,6 +23,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ContrastColorUtil;
@@ -58,15 +59,6 @@
/** Spacing to be applied between views. */
private final int mSpacing;
- /** Horizontal padding of smart reply buttons if all of them use only one line of text. */
- private final int mSingleLineButtonPaddingHorizontal;
-
- /** Horizontal padding of smart reply buttons if at least one of them uses two lines of text. */
- private final int mDoubleLineButtonPaddingHorizontal;
-
- /** Increase in width of a smart reply button as a result of using two lines instead of one. */
- private final int mSingleToDoubleLineButtonWidthIncrease;
-
private final BreakIterator mBreakIterator;
private PriorityQueue<Button> mCandidateButtonQueueForSqueezing;
@@ -114,8 +106,6 @@
mDefaultBackgroundColor);
int spacing = 0;
- int singleLineButtonPaddingHorizontal = 0;
- int doubleLineButtonPaddingHorizontal = 0;
int strokeWidth = 0;
final TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.SmartReplyView,
@@ -125,10 +115,6 @@
int attr = arr.getIndex(i);
if (attr == R.styleable.SmartReplyView_spacing) {
spacing = arr.getDimensionPixelSize(i, 0);
- } else if (attr == R.styleable.SmartReplyView_singleLineButtonPaddingHorizontal) {
- singleLineButtonPaddingHorizontal = arr.getDimensionPixelSize(i, 0);
- } else if (attr == R.styleable.SmartReplyView_doubleLineButtonPaddingHorizontal) {
- doubleLineButtonPaddingHorizontal = arr.getDimensionPixelSize(i, 0);
} else if (attr == R.styleable.SmartReplyView_buttonStrokeWidth) {
strokeWidth = arr.getDimensionPixelSize(i, 0);
}
@@ -137,10 +123,6 @@
mStrokeWidth = strokeWidth;
mSpacing = spacing;
- mSingleLineButtonPaddingHorizontal = singleLineButtonPaddingHorizontal;
- mDoubleLineButtonPaddingHorizontal = doubleLineButtonPaddingHorizontal;
- mSingleToDoubleLineButtonWidthIncrease =
- 2 * (doubleLineButtonPaddingHorizontal - singleLineButtonPaddingHorizontal);
mBreakIterator = BreakIterator.getLineInstance();
@@ -222,6 +204,12 @@
return new LayoutParams(params.width, params.height);
}
+ private void clearLayoutLineCount(View view) {
+ if (view instanceof TextView) {
+ ((TextView) view).nullLayouts();
+ }
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int targetWidth = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED
@@ -237,8 +225,7 @@
SmartSuggestionMeasures accumulatedMeasures = new SmartSuggestionMeasures(
mPaddingLeft + mPaddingRight,
- 0 /* maxChildHeight */,
- mSingleLineButtonPaddingHorizontal);
+ 0 /* maxChildHeight */);
int displayedChildCount = 0;
// Set up a list of suggestions where actions come before replies. Note that the Buttons
@@ -268,8 +255,7 @@
continue;
}
- child.setPadding(accumulatedMeasures.mButtonPaddingHorizontal, child.getPaddingTop(),
- accumulatedMeasures.mButtonPaddingHorizontal, child.getPaddingBottom());
+ clearLayoutLineCount(child);
child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
coveredSuggestions.add(child);
@@ -299,18 +285,6 @@
accumulatedMeasures.mMaxChildHeight =
Math.max(accumulatedMeasures.mMaxChildHeight, childHeight);
- // Do we need to increase the number of lines in smart reply buttons to two?
- final boolean increaseToTwoLines =
- (accumulatedMeasures.mButtonPaddingHorizontal
- == mSingleLineButtonPaddingHorizontal)
- && (lineCount == 2 || accumulatedMeasures.mMeasuredWidth > targetWidth);
- if (increaseToTwoLines) {
- accumulatedMeasures.mMeasuredWidth +=
- (displayedChildCount + 1) * mSingleToDoubleLineButtonWidthIncrease;
- accumulatedMeasures.mButtonPaddingHorizontal =
- mDoubleLineButtonPaddingHorizontal;
- }
-
// If the last button doesn't fit into the remaining width, try squeezing preceding
// smart reply buttons.
if (accumulatedMeasures.mMeasuredWidth > targetWidth) {
@@ -372,18 +346,11 @@
mCandidateButtonQueueForSqueezing.clear();
// Finally, we need to re-measure some buttons.
- remeasureButtonsIfNecessary(accumulatedMeasures.mButtonPaddingHorizontal,
- accumulatedMeasures.mMaxChildHeight);
+ remeasureButtonsIfNecessary(accumulatedMeasures.mMaxChildHeight);
int buttonHeight = Math.max(getSuggestedMinimumHeight(), mPaddingTop
+ accumulatedMeasures.mMaxChildHeight + mPaddingBottom);
- // Set the corner radius to half the button height to make the side of the buttons look like
- // a semicircle.
- for (View smartSuggestionButton : smartSuggestions) {
- setCornerRadius((Button) smartSuggestionButton, ((float) buttonHeight) / 2);
- }
-
setMeasuredDimension(
resolveSize(Math.max(getSuggestedMinimumWidth(),
accumulatedMeasures.mMeasuredWidth),
@@ -411,18 +378,14 @@
private static class SmartSuggestionMeasures {
int mMeasuredWidth = -1;
int mMaxChildHeight = -1;
- int mButtonPaddingHorizontal = -1;
- SmartSuggestionMeasures(int measuredWidth, int maxChildHeight,
- int buttonPaddingHorizontal) {
+ SmartSuggestionMeasures(int measuredWidth, int maxChildHeight) {
this.mMeasuredWidth = measuredWidth;
this.mMaxChildHeight = maxChildHeight;
- this.mButtonPaddingHorizontal = buttonPaddingHorizontal;
}
public SmartSuggestionMeasures clone() {
- return new SmartSuggestionMeasures(
- mMeasuredWidth, mMaxChildHeight, mButtonPaddingHorizontal);
+ return new SmartSuggestionMeasures(mMeasuredWidth, mMaxChildHeight);
}
}
@@ -553,17 +516,11 @@
private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) {
int oldWidth = button.getMeasuredWidth();
- if (button.getPaddingLeft() != mDoubleLineButtonPaddingHorizontal) {
- // Correct for the fact that the button was laid out with single-line horizontal
- // padding.
- oldWidth += mSingleToDoubleLineButtonWidthIncrease;
- }
// Re-measure the squeezed smart reply button.
- button.setPadding(mDoubleLineButtonPaddingHorizontal, button.getPaddingTop(),
- mDoubleLineButtonPaddingHorizontal, button.getPaddingBottom());
+ clearLayoutLineCount(button);
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
- 2 * mDoubleLineButtonPaddingHorizontal + textWidth
+ button.getPaddingLeft() + button.getPaddingRight() + textWidth
+ getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
button.measure(widthMeasureSpec, heightMeasureSpec);
@@ -579,8 +536,7 @@
}
}
- private void remeasureButtonsIfNecessary(
- int buttonPaddingHorizontal, int maxChildHeight) {
+ private void remeasureButtonsIfNecessary(int maxChildHeight) {
final int maxChildHeightMeasure =
MeasureSpec.makeMeasureSpec(maxChildHeight, MeasureSpec.EXACTLY);
@@ -602,24 +558,7 @@
newWidth = Integer.MAX_VALUE;
}
- // Re-measure reason 2: The button's horizontal padding is incorrect (because it was
- // measured with the wrong number of lines).
- if (child.getPaddingLeft() != buttonPaddingHorizontal) {
- requiresNewMeasure = true;
- if (newWidth != Integer.MAX_VALUE) {
- if (buttonPaddingHorizontal == mSingleLineButtonPaddingHorizontal) {
- // Change padding (2->1 line).
- newWidth -= mSingleToDoubleLineButtonWidthIncrease;
- } else {
- // Change padding (1->2 lines).
- newWidth += mSingleToDoubleLineButtonWidthIncrease;
- }
- }
- child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
- buttonPaddingHorizontal, child.getPaddingBottom());
- }
-
- // Re-measure reason 3: The button's height is less than the max height of all buttons
+ // Re-measure reason 2: The button's height is less than the max height of all buttons
// (all should have the same height).
if (child.getMeasuredHeight() != maxChildHeight) {
requiresNewMeasure = true;
@@ -725,23 +664,6 @@
button.setTextColor(mCurrentTextColor);
}
- private void setCornerRadius(Button button, float radius) {
- Drawable drawable = button.getBackground();
- if (drawable instanceof RippleDrawable) {
- // Mutate in case other notifications are using this drawable.
- drawable = drawable.mutate();
- RippleDrawable ripple = (RippleDrawable) drawable;
- Drawable inset = ripple.getDrawable(0);
- if (inset instanceof InsetDrawable) {
- Drawable background = ((InsetDrawable) inset).getDrawable();
- if (background instanceof GradientDrawable) {
- GradientDrawable gradientDrawable = (GradientDrawable) background;
- gradientDrawable.setCornerRadius(radius);
- }
- }
- }
- }
-
enum SmartButtonType {
REPLY,
ACTION
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
index 644addf..5e9bae9 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
@@ -20,13 +20,10 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.text.TextPaint;
import android.util.MathUtils;
import android.view.View;
-import android.widget.TextView;
import androidx.annotation.ColorInt;
-import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
@@ -39,12 +36,10 @@
@ColorInt private final int mUnselectedColor;
@ColorInt private final int mSelectedColor;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
private WalletCardCarousel mCardCarousel;
DotIndicatorDecoration(Context context) {
super();
-
mUnselectedRadius =
context.getResources().getDimensionPixelSize(
R.dimen.card_carousel_dot_unselected_radius);
@@ -53,13 +48,8 @@
R.dimen.card_carousel_dot_selected_radius);
mDotMargin = context.getResources().getDimensionPixelSize(R.dimen.card_carousel_dot_margin);
- TextView textView = new TextView(context);
- mTextPaint.set(textView.getPaint());
- // Text color is not copied from text appearance.
- mTextPaint.setColor(ContextCompat.getColor(context, R.color.GM2_blue_600));
-
- mUnselectedColor = ContextCompat.getColor(context, R.color.GM2_grey_300);
- mSelectedColor = ContextCompat.getColor(context, R.color.GM2_blue_600);
+ mUnselectedColor = context.getColor(com.android.internal.R.color.system_neutral1_300);
+ mSelectedColor = context.getColor(com.android.internal.R.color.system_neutral1_0);
}
@Override
@@ -107,9 +97,9 @@
int i = isLayoutLtr() ? itemsDrawn : itemCount - itemsDrawn - 1;
if (isSelectedItem(i)) {
- drawSelectedDot(canvas, interpolatedProgress, i);
+ drawSelectedDot(canvas, interpolatedProgress);
} else if (isNextItemInScrollingDirection(i)) {
- drawFadingUnselectedDot(canvas, interpolatedProgress, i);
+ drawFadingUnselectedDot(canvas, interpolatedProgress);
} else {
drawUnselectedDot(canvas);
}
@@ -121,7 +111,7 @@
this.mCardCarousel = null; // No need to hold a reference.
}
- private void drawSelectedDot(Canvas canvas, float progress, int position) {
+ private void drawSelectedDot(Canvas canvas, float progress) {
// Divide progress by 2 because the other half of the animation is done by
// drawFadingUnselectedDot.
mPaint.setColor(
@@ -132,13 +122,13 @@
canvas.translate(radius * 2, 0);
}
- private void drawFadingUnselectedDot(Canvas canvas, float progress, int position) {
+ private void drawFadingUnselectedDot(Canvas canvas, float progress) {
// Divide progress by 2 because the first half of the animation is done by drawSelectedDot.
int blendedColor =
ColorUtils.blendARGB(
mUnselectedColor, mSelectedColor, progress / 2);
mPaint.setColor(getTransitionAdjustedColor(blendedColor));
- float radius = MathUtils.lerp(mSelectedRadius, mUnselectedRadius, progress / 2);
+ float radius = MathUtils.lerp(mUnselectedRadius, mSelectedColor, progress / 2);
canvas.drawCircle(radius, 0, radius, mPaint);
canvas.translate(radius * 2, 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index ac8b16a..66bd48b 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -17,6 +17,7 @@
package com.android.systemui.wallet.ui;
import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.service.quickaccesswallet.QuickAccessWalletClient;
@@ -95,7 +96,7 @@
}
setTitle("");
getActionBar().setDisplayHomeAsUpEnabled(true);
- getActionBar().setHomeAsUpIndicator(R.drawable.ic_close);
+ getActionBar().setHomeAsUpIndicator(getHomeIndicatorDrawable());
getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close);
WalletView walletView = requireViewById(R.id.wallet_view);
mWalletScreenController = new WalletScreenController(
@@ -175,4 +176,10 @@
mWalletScreenController.onDismissed();
super.onDestroy();
}
+
+ private Drawable getHomeIndicatorDrawable() {
+ Drawable drawable = getDrawable(R.drawable.ic_close);
+ drawable.setTint(getColor(com.android.internal.R.color.system_neutral1_300));
+ return drawable;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index ec62981..b57d937 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -234,7 +234,9 @@
mWalletView.show();
mWalletView.hideErrorMessage();
int iconSizePx =
- mContext.getResources().getDimensionPixelSize(R.dimen.wallet_view_header_icon_size);
+ mContext
+ .getResources()
+ .getDimensionPixelSize(R.dimen.wallet_screen_header_icon_size);
GetWalletCardsRequest request =
new GetWalletCardsRequest(cardWidthPx, cardHeightPx, iconSizePx, MAX_CARDS);
mWalletClient.getWalletCards(mExecutor, request, this);
@@ -340,7 +342,11 @@
@Override
public CharSequence getLabel() {
- return mWalletCard.getCardLabel();
+ CharSequence label = mWalletCard.getCardLabel();
+ if (label == null) {
+ return "";
+ }
+ return label;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index c547bb3..e42ce6a 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -37,6 +37,7 @@
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import java.util.List;
@@ -100,7 +101,7 @@
public void onCardScroll(WalletCardViewInfo centerCard, WalletCardViewInfo nextCard,
float percentDistanceFromCenter) {
CharSequence centerCardText = getLabelText(centerCard);
- Drawable centerCardIcon = centerCard.getIcon();
+ Drawable centerCardIcon = getHeaderIcon(mContext, centerCard);
if (!TextUtils.equals(mCenterCardText, centerCardText)) {
mCenterCardText = centerCardText;
mCardLabel.setText(centerCardText);
@@ -133,7 +134,8 @@
mCardCarouselContainer.setVisibility(VISIBLE);
mErrorView.setVisibility(GONE);
mEmptyStateView.setVisibility(GONE);
- renderHeaderIconAndActionButton(data.get(selectedIndex), isDeviceLocked);
+ mIcon.setImageDrawable(getHeaderIcon(mContext, data.get(selectedIndex)));
+ renderActionButton(data.get(selectedIndex), isDeviceLocked);
if (shouldAnimate) {
animateViewsShown(mIcon, mCardLabel, mActionButton);
}
@@ -220,10 +222,15 @@
return mCardLabel;
}
- private void renderHeaderIconAndActionButton(WalletCardViewInfo walletCard, boolean isLocked) {
- mIcon.setImageDrawable(walletCard.getIcon());
- mIcon.setVisibility(VISIBLE);
- renderActionButton(walletCard, isLocked);
+ @Nullable
+ private static Drawable getHeaderIcon(Context context, WalletCardViewInfo walletCard) {
+ Drawable icon = walletCard.getIcon();
+ if (icon != null) {
+ icon.setTint(
+ Utils.getColorAttrDefaultColor(
+ context, com.android.internal.R.attr.colorAccentPrimary));
+ }
+ return icon;
}
private void renderActionButton(WalletCardViewInfo walletCard, boolean isDeviceLocked) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index 1d9eaae..77286b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -37,7 +37,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import org.junit.Before;
@@ -67,9 +68,12 @@
@Mock
private ModeSwitchesController mModeSwitchesController;
@Mock
- private NavigationModeController mNavigationModeController;
+ private SysUiState mSysUiState;
@Mock
private IRemoteMagnificationAnimationCallback mAnimationCallback;
+ @Mock
+ private OverviewProxyService mOverviewProxyService;
+
private IWindowMagnificationConnection mIWindowMagnificationConnection;
private WindowMagnification mWindowMagnification;
@@ -83,8 +87,8 @@
}).when(mAccessibilityManager).setWindowMagnificationConnection(
any(IWindowMagnificationConnection.class));
mWindowMagnification = new WindowMagnification(getContext(),
- getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
- mNavigationModeController);
+ getContext().getMainThreadHandler(), mCommandQueue,
+ mModeSwitchesController, mSysUiState, mOverviewProxyService);
mWindowMagnification.mAnimationControllerSupplier = new FakeAnimationControllerSupplier(
mContext.getSystemService(DisplayManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 4e4c33a..045fb57f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -41,6 +41,7 @@
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.model.SysUiState;
import org.junit.After;
import org.junit.Before;
@@ -83,6 +84,8 @@
IRemoteMagnificationAnimationCallback mAnimationCallback;
@Mock
IRemoteMagnificationAnimationCallback mAnimationCallback2;
+ @Mock
+ SysUiState mSysUiState;
private SpyWindowMagnificationController mController;
private WindowMagnificationController mSpyController;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@@ -98,7 +101,7 @@
mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2;
mController = new SpyWindowMagnificationController(mContext, mHandler,
mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(),
- mWindowMagnifierCallback);
+ mWindowMagnifierCallback, mSysUiState);
mSpyController = mController.getSpyController();
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
mContext, mController, newValueAnimator());
@@ -394,6 +397,13 @@
verify(mSpyController).onConfigurationChanged(100);
}
+ @Test
+ public void updateSysUiStateFlag_passThrough() {
+ mWindowMagnificationAnimationController.updateSysUiStateFlag();
+
+ verify(mSpyController).updateSysUIStateFlag();
+ }
+
private void verifyFinalSpec(float expectedScale, float expectedCenterX,
float expectedCenterY) {
assertEquals(expectedScale, mController.getScale(), 0f);
@@ -440,9 +450,9 @@
SpyWindowMagnificationController(Context context, Handler handler,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
- WindowMagnifierCallback callback) {
+ WindowMagnifierCallback callback, SysUiState sysUiState) {
super(context, handler, sfVsyncFrameProvider, mirrorWindowControl, transaction,
- callback);
+ callback, sysUiState);
mSpyController = Mockito.mock(WindowMagnificationController.class);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 7d617db..b8734df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -17,12 +17,15 @@
package com.android.systemui.accessibility;
import static android.view.Choreographer.FrameCallback;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+import static android.view.WindowInsets.Type.systemGestures;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItems;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -33,6 +36,9 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -40,49 +46,60 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Handler;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.testing.TestableResources;
+import android.text.TextUtils;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.util.leak.ReferenceTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-@SmallTest
+@LargeTest
@RunWith(AndroidTestingRunner.class)
public class WindowMagnificationControllerTest extends SysuiTestCase {
+ private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
@Mock
- Handler mHandler;
+ private Handler mHandler;
@Mock
- SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+ private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
- MirrorWindowControl mMirrorWindowControl;
+ private MirrorWindowControl mMirrorWindowControl;
@Mock
- WindowMagnifierCallback mWindowMagnifierCallback;
- @Mock
- SurfaceControl.Transaction mTransaction;
- @Mock
+ private WindowMagnifierCallback mWindowMagnifierCallback;
+ @Mock (answer = Answers.RETURNS_DEEP_STUBS)
+ private SurfaceControl.Transaction mTransaction;
private WindowManager mWindowManager;
+ private SysUiState mSysUiState = new SysUiState();
private Resources mResources;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
@@ -93,37 +110,30 @@
MockitoAnnotations.initMocks(this);
mContext = Mockito.spy(getContext());
mInstrumentation = InstrumentationRegistry.getInstrumentation();
- WindowManager wm = mContext.getSystemService(WindowManager.class);
- doAnswer(invocation ->
- wm.getMaximumWindowMetrics()
- ).when(mWindowManager).getMaximumWindowMetrics();
- doAnswer(invocation ->
- wm.getCurrentWindowMetrics()
- ).when(mWindowManager).getCurrentWindowMetrics();
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ mWindowManager = spy(new TestableWindowManager(wm));
+
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
doAnswer(invocation -> {
- mMirrorView = invocation.getArgument(0);
- WindowManager.LayoutParams lp = invocation.getArgument(1);
- mMirrorView.setLayoutParams(lp);
- return null;
- }).when(mWindowManager).addView(any(View.class), any(WindowManager.LayoutParams.class));
- doAnswer(invocation -> {
- mMirrorView = null;
- return null;
- }).when(mWindowManager).removeView(any(View.class));
- doAnswer(invocation -> {
FrameCallback callback = invocation.getArgument(0);
callback.doFrame(0);
return null;
}).when(mSfVsyncFrameProvider).postFrameCallback(
any(FrameCallback.class));
- when(mTransaction.remove(any())).thenReturn(mTransaction);
- when(mTransaction.setGeometry(any(), any(), any(),
- anyInt())).thenReturn(mTransaction);
+ doAnswer(invocation -> {
+ final Runnable runnable = invocation.getArgument(0);
+ runnable.run();
+ return null;
+ }).when(mHandler).post(
+ any(Runnable.class));
+
+ mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
+
mResources = getContext().getOrCreateTestableResources().getResources();
mWindowMagnificationController = new WindowMagnificationController(mContext,
mHandler, mSfVsyncFrameProvider,
- mMirrorWindowControl, mTransaction, mWindowMagnifierCallback);
+ mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
+
verify(mMirrorWindowControl).setWindowDelegate(
any(MirrorWindowControl.MirrorWindowDelegate.class));
}
@@ -135,12 +145,21 @@
}
@Test
- public void enableWindowMagnification_showControl() {
+ public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN);
});
+
verify(mMirrorWindowControl).showControl();
+ ArgumentCaptor<Rect> boundsCaptor = ArgumentCaptor.forClass(Rect.class);
+ verify(mWindowMagnifierCallback,
+ timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onWindowMagnifierBoundsChanged(
+ eq(mContext.getDisplayId()), boundsCaptor.capture());
+ final Rect actualBounds = new Rect();
+ mMirrorView.getBoundsOnScreen(actualBounds);
+ assertEquals(actualBounds, boundsCaptor.getValue());
+
}
@Test
@@ -158,6 +177,25 @@
}
@Test
+ public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() {
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ bounds.bottom);
+ });
+ ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.deleteWindowMagnification();
+ });
+
+ verify(mMirrorWindowControl).destroyControl();
+ assertFalse(hasMagnificationOverlapFlag());
+ }
+
+ @Test
public void moveMagnifier_schedulesFrame() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -210,7 +248,8 @@
});
assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
- verify(mWindowManager).updateViewLayout(any(), any());
+ // The first invocation is called when the surface is created.
+ verify(mWindowManager, times(2)).updateViewLayout(any(), any());
}
@Test
@@ -318,17 +357,6 @@
}
@Test
- public void onNavigationModeChanged_updateMirrorViewLayout() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.onNavigationModeChanged(NAV_BAR_MODE_GESTURAL);
- });
-
- verify(mWindowManager).updateViewLayout(eq(mMirrorView), any());
- }
-
- @Test
public void enableWindowMagnification_hasA11yWindowTitle() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -353,16 +381,12 @@
final TestableResources testableResources = getContext().getOrCreateTestableResources();
testableResources.addOverride(com.android.internal.R.string.android_system_label,
newA11yWindowTitle);
- when(mContext.getResources()).thenReturn(testableResources.getResources());
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
});
- ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
- WindowManager.LayoutParams.class);
- verify(mWindowManager).updateViewLayout(eq(mMirrorView), paramsArgumentCaptor.capture());
- assertEquals(newA11yWindowTitle, paramsArgumentCaptor.getValue().accessibilityTitle);
+ assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle()));
}
@Test
@@ -386,4 +410,95 @@
}
fail("mMirrorView scale is not changed");
}
+
+ @Test
+ public void moveWindowMagnificationToTheBottom_enabled_overlapFlagIsTrue() {
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.moveWindowMagnifier(0, bounds.height());
+ });
+
+ ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag());
+ }
+
+ private CharSequence getAccessibilityWindowTitle() {
+ if (mMirrorView == null) {
+ return null;
+ }
+ WindowManager.LayoutParams layoutParams =
+ (WindowManager.LayoutParams) mMirrorView.getLayoutParams();
+ return layoutParams.accessibilityTitle;
+ }
+
+ private boolean hasMagnificationOverlapFlag() {
+ return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0;
+ }
+
+ private class TestableWindowManager implements WindowManager {
+
+ private final WindowManager mWindowManager;
+
+ TestableWindowManager(WindowManager windowManager) {
+ mWindowManager = windowManager;
+ }
+
+ @Override
+ public Display getDefaultDisplay() {
+ return mWindowManager.getDefaultDisplay();
+ }
+
+ @Override
+ public void removeViewImmediate(View view) {
+ mWindowManager.removeViewImmediate(view);
+ }
+
+ @Override
+ public void requestAppKeyboardShortcuts(KeyboardShortcutsReceiver receiver, int deviceId) {
+ mWindowManager.requestAppKeyboardShortcuts(receiver, deviceId);
+ }
+
+ @Override
+ public Region getCurrentImeTouchRegion() {
+ return mWindowManager.getCurrentImeTouchRegion();
+ }
+
+ @Override
+ public void addView(View view, ViewGroup.LayoutParams params) {
+ mMirrorView = view;
+ mWindowManager.addView(view, params);
+ }
+
+ @Override
+ public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
+ mWindowManager.updateViewLayout(view, params);
+ }
+
+ @Override
+ public void removeView(View view) {
+ mMirrorView = null;
+ mWindowManager.removeView(view);
+ }
+
+ @Override
+ public WindowMetrics getCurrentWindowMetrics() {
+ final Insets systemGesturesInsets = Insets.of(0, 0, 0, 10);
+ final WindowInsets insets = new WindowInsets.Builder()
+ .setInsets(systemGestures(), systemGesturesInsets)
+ .build();
+ final WindowMetrics windowMetrics = new WindowMetrics(
+ mWindowManager.getCurrentWindowMetrics().getBounds(), insets);
+ return windowMetrics;
+ }
+
+ @Override
+ public WindowMetrics getMaximumWindowMetrics() {
+ return mWindowManager.getMaximumWindowMetrics();
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index 7833114..6ef7cc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -16,15 +16,21 @@
package com.android.systemui.accessibility;
+import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -36,12 +42,14 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -50,17 +58,21 @@
@TestableLooper.RunWithLooper
public class WindowMagnificationTest extends SysuiTestCase {
+ private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
@Mock
private AccessibilityManager mAccessibilityManager;
@Mock
private ModeSwitchesController mModeSwitchesController;
@Mock
- private NavigationModeController mNavigationModeController;
+ private SysUiState mSysUiState;
@Mock
private IWindowMagnificationConnectionCallback mConnectionCallback;
+ @Mock
+ private OverviewProxyService mOverviewProxyService;
+
private CommandQueue mCommandQueue;
private WindowMagnification mWindowMagnification;
-
+ private OverviewProxyListener mOverviewProxyListener;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -72,11 +84,18 @@
}).when(mAccessibilityManager).setWindowMagnificationConnection(
any(IWindowMagnificationConnection.class));
+ when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+
mCommandQueue = new CommandQueue(getContext());
mWindowMagnification = new WindowMagnification(getContext(),
getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
- mNavigationModeController);
+ mSysUiState, mOverviewProxyService);
mWindowMagnification.start();
+
+ final ArgumentCaptor<OverviewProxyListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(OverviewProxyListener.class);
+ verify(mOverviewProxyService).addCallback(listenerArgumentCaptor.capture());
+ mOverviewProxyListener = listenerArgumentCaptor.getValue();
}
@Test
@@ -99,10 +118,9 @@
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.onWindowMagnifierBoundsChanged(Display.DEFAULT_DISPLAY, testBounds);
+ mWindowMagnification.onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds);
- verify(mConnectionCallback).onWindowMagnifierBoundsChanged(Display.DEFAULT_DISPLAY,
- testBounds);
+ verify(mConnectionCallback).onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds);
}
@Test
@@ -111,10 +129,9 @@
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.onPerformScaleAction(Display.DEFAULT_DISPLAY, newScale);
+ mWindowMagnification.onPerformScaleAction(TEST_DISPLAY, newScale);
- verify(mConnectionCallback).onPerformScaleAction(eq(Display.DEFAULT_DISPLAY),
- eq(newScale));
+ verify(mConnectionCallback).onPerformScaleAction(TEST_DISPLAY, newScale);
}
@Test
@@ -122,9 +139,9 @@
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.onAccessibilityActionPerformed(Display.DEFAULT_DISPLAY);
+ mWindowMagnification.onAccessibilityActionPerformed(TEST_DISPLAY);
- verify(mConnectionCallback).onAccessibilityActionPerformed(eq(Display.DEFAULT_DISPLAY));
+ verify(mConnectionCallback).onAccessibilityActionPerformed(TEST_DISPLAY);
}
@Test
@@ -135,4 +152,42 @@
verify(mModeSwitchesController).onConfigurationChanged(anyInt());
}
+
+ @Test
+ public void overviewProxyIsConnected_noController_resetFlag() {
+ mOverviewProxyListener.onConnectionChanged(true);
+
+ verify(mSysUiState).setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false);
+ verify(mSysUiState).commitUpdate(mContext.getDisplayId());
+ }
+
+ @Test
+ public void overviewProxyIsConnected_controllerIsAvailable_updateSysUiStateFlag() {
+ final WindowMagnificationAnimationController mController = mock(
+ WindowMagnificationAnimationController.class);
+ mWindowMagnification.mAnimationControllerSupplier = new FakeAnimationControllerSupplier(
+ mContext.getSystemService(DisplayManager.class), mController);
+ mWindowMagnification.mAnimationControllerSupplier.get(TEST_DISPLAY);
+
+ mOverviewProxyListener.onConnectionChanged(true);
+
+ verify(mController).updateSysUiStateFlag();
+ }
+
+ private static class FakeAnimationControllerSupplier extends
+ DisplayIdIndexSupplier<WindowMagnificationAnimationController> {
+
+ private final WindowMagnificationAnimationController mController;
+
+ FakeAnimationControllerSupplier(DisplayManager displayManager,
+ WindowMagnificationAnimationController controller) {
+ super(displayManager);
+ mController = controller;
+ }
+
+ @Override
+ protected WindowMagnificationAnimationController createInstance(Display display) {
+ return mController;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
new file mode 100644
index 0000000..46c930f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.testing.AndroidTestingRunner;
+import android.text.SpannableStringBuilder;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** Tests for {@link AnnotationLinkSpan}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class AnnotationLinkSpanTest extends SysuiTestCase {
+
+ private AnnotationLinkSpan.LinkInfo mLinkInfo;
+
+ @Before
+ public void setUp() {
+ mLinkInfo = new AnnotationLinkSpan.LinkInfo(
+ AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
+ mock(View.OnClickListener.class));
+ }
+
+ @Test
+ public void linkifyText_textAttachedWithSpan() {
+ final CharSequence text = getContext().getText(
+ R.string.accessibility_floating_button_migration_tooltip);
+ final SpannableStringBuilder builder =
+ (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, mLinkInfo);
+ final int AnnotationLinkSpanNum =
+ builder.getSpans(/* queryStart= */ 0, builder.length(),
+ AnnotationLinkSpan.class).length;
+
+ assertThat(AnnotationLinkSpanNum).isEqualTo(1);
+ }
+
+ @Test
+ public void linkifyText_withoutAnnotationTag_textWithoutSpan() {
+ final CharSequence text = "text without any annotation tag";
+ final SpannableStringBuilder builder =
+ (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, mLinkInfo);
+ final int AnnotationLinkSpanNum =
+ builder.getSpans(/* queryStart= */ 0, builder.length(),
+ AnnotationLinkSpan.class).length;
+
+ assertThat(AnnotationLinkSpanNum).isEqualTo(0);
+ }
+
+ @Test
+ public void linkifyText_twoLinkInfoWithSameAnnotation_listenerInvoked() {
+ final AtomicBoolean isClicked = new AtomicBoolean(false);
+ final CharSequence text = getContext().getText(
+ R.string.accessibility_floating_button_migration_tooltip);
+ final View.OnClickListener firstListener = v -> isClicked.set(true);
+ final AnnotationLinkSpan.LinkInfo firstLinkInfo = new AnnotationLinkSpan.LinkInfo(
+ AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION, firstListener);
+
+ final SpannableStringBuilder builder =
+ (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, firstLinkInfo, mLinkInfo);
+ final AnnotationLinkSpan[] firstAnnotationLinkSpan =
+ builder.getSpans(/* queryStart= */ 0, builder.length(),
+ AnnotationLinkSpan.class);
+ firstAnnotationLinkSpan[0].onClick(mock(View.class));
+
+ assertThat(isClicked.get()).isTrue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java
new file mode 100644
index 0000000..6db5761
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.MotionEventHelper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link BaseTooltipView}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class BaseTooltipViewTest extends SysuiTestCase {
+
+ @Mock
+ private WindowManager mWindowManager;
+
+ private AccessibilityFloatingMenuView mMenuView;
+ private BaseTooltipView mToolTipView;
+
+ private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
+ mWindowManager).getMaximumWindowMetrics();
+ mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
+ mMenuView = new AccessibilityFloatingMenuView(mContext);
+ mToolTipView = new BaseTooltipView(mContext, mMenuView);
+ }
+
+ @Test
+ public void showToolTipView_success() {
+ mToolTipView.show();
+
+ verify(mWindowManager).addView(eq(mToolTipView), any(WindowManager.LayoutParams.class));
+ }
+
+ @Test
+ public void touchOutsideWhenToolTipViewShown_dismiss() {
+ final MotionEvent outsideEvent =
+ mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0,
+ /* eventTime= */1,
+ MotionEvent.ACTION_OUTSIDE,
+ /* x= */ 0,
+ /* y= */ 0);
+
+ mToolTipView.show();
+ mToolTipView.dispatchTouchEvent(outsideEvent);
+
+ verify(mWindowManager).removeView(mToolTipView);
+ }
+
+ @Test
+ public void getAccessibilityActionList_matchResult() {
+ final AccessibilityNodeInfo infos = new AccessibilityNodeInfo();
+ mToolTipView.onInitializeAccessibilityNodeInfo(infos);
+
+ assertThat(infos.getActionList().size()).isEqualTo(1);
+ }
+
+ @Test
+ public void accessibilityAction_dismiss_success() {
+ final BaseTooltipView tooltipView =
+ spy(new BaseTooltipView(mContext, mMenuView));
+
+ final boolean isActionPerformed =
+ tooltipView.performAccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS.getId(),
+ /* arguments= */ null);
+
+ assertThat(isActionPerformed).isTrue();
+ verify(tooltipView).hide();
+ }
+
+ @After
+ public void tearDown() {
+ mToolTipView.hide();
+ mMotionEventHelper.recycleEvents();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java
new file mode 100644
index 0000000..41b948f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.MotionEventHelper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link DockTooltipView}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DockTooltipViewTest extends SysuiTestCase {
+
+ @Mock
+ private WindowManager mWindowManager;
+
+ private AccessibilityFloatingMenuView mMenuView;
+ private DockTooltipView mDockTooltipView;
+ private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
+ mWindowManager).getMaximumWindowMetrics();
+ mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
+ mMenuView = spy(new AccessibilityFloatingMenuView(mContext));
+ mDockTooltipView = new DockTooltipView(mContext, mMenuView);
+ }
+
+ @Test
+ public void showTooltip_success() {
+ mDockTooltipView.show();
+
+ verify(mMenuView).startTranslateXAnimation();
+ verify(mWindowManager).addView(eq(mDockTooltipView), any(WindowManager.LayoutParams.class));
+ }
+
+ @Test
+ public void hideTooltip_success() {
+ mDockTooltipView.show();
+ mDockTooltipView.hide();
+
+ verify(mMenuView).stopTranslateXAnimation();
+ verify(mWindowManager).removeView(mDockTooltipView);
+ }
+
+ @Test
+ public void touchOutsideWhenToolTipViewShown_stopAnimation() {
+ final MotionEvent outsideEvent =
+ mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0,
+ /* eventTime= */ 1,
+ MotionEvent.ACTION_OUTSIDE,
+ /* x= */ 0,
+ /* y= */ 0);
+
+ mDockTooltipView.show();
+ mDockTooltipView.dispatchTouchEvent(outsideEvent);
+
+ verify(mMenuView).stopTranslateXAnimation();
+ }
+
+ @After
+ public void tearDown() {
+ mMotionEventHelper.recycleEvents();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java
new file mode 100644
index 0000000..c5bd2fe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link MigrationTooltipView}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MigrationTooltipViewTest extends SysuiTestCase {
+
+ private TextView mTextView;
+
+ @Before
+ public void setUp() {
+ final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext);
+ final MigrationTooltipView toolTipView = new MigrationTooltipView(mContext, menuView);
+ mTextView = toolTipView.findViewById(R.id.text);
+ }
+
+ @Test
+ public void onCreate_setLinkMovementMethod() {
+ assertThat(mTextView.getMovementMethod()).isInstanceOf(LinkMovementMethod.class);
+ }
+
+ @Test
+ public void onCreate_setDescription_matchTextAndSpanNum() {
+ final CharSequence expectedTextWithoutSpan =
+ AnnotationLinkSpan.linkify(mContext.getText(
+ R.string.accessibility_floating_button_migration_tooltip)).toString();
+ final SpannableString spannableString = (SpannableString) mTextView.getText();
+ final int AnnotationLinkSpanNum =
+ spannableString.getSpans(/* queryStart= */ 0, spannableString.length(),
+ AnnotationLinkSpan.class).length;
+
+ assertThat(AnnotationLinkSpanNum).isEqualTo(1);
+ assertThat(mTextView.getText().toString().contentEquals(expectedTextWithoutSpan)).isTrue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 2530cfd..46c1848 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -70,6 +70,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -94,6 +95,8 @@
@Mock
private WindowManager mWindowManager;
@Mock
+ private UdfpsHbmCallback mHbmCallback;
+ @Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private StatusBar mStatusBar;
@@ -174,7 +177,8 @@
mPowerManager,
mAccessibilityManager,
mScreenLifecycle,
- mVibrator);
+ mVibrator,
+ Optional.of(mHbmCallback));
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 80716f9..003368d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -39,6 +39,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.IWindowManager;
+import android.view.View;
import android.view.WindowManagerPolicyConstants;
import androidx.test.filters.SmallTest;
@@ -172,6 +173,44 @@
}
@Test
+ public void testShouldLogClose_backButton() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+ dialog.onBackPressed();
+ mTestableLooper.processAllMessages();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_BACK);
+ }
+
+ @Test
+ public void testShouldLogOnTapOutside() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+ View container = dialog.findViewById(com.android.systemui.R.id.global_actions_container);
+ container.callOnClick();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ }
+
+ @Test
public void testShouldLogBugreportPress() throws InterruptedException {
GlobalActionsDialog.BugReportAction bugReportAction =
mGlobalActionsDialogLite.makeBugReportActionForTesting();
@@ -286,4 +325,44 @@
assertThat(mGlobalActionsDialogLite.mOverflowItems).isEmpty();
assertThat(mGlobalActionsDialogLite.mPowerItems).isEmpty();
}
+
+ @Test
+ public void testShouldLogLockdownPress() {
+ GlobalActionsDialogLite.LockDownAction lockDownAction =
+ mGlobalActionsDialogLite.new LockDownAction();
+ lockDownAction.onPress();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_LOCKDOWN_PRESS);
+ }
+
+ @Test
+ public void testShouldLogShutdownPress() {
+ GlobalActionsDialogLite.ShutDownAction shutDownAction =
+ mGlobalActionsDialogLite.new ShutDownAction();
+ shutDownAction.onPress();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SHUTDOWN_PRESS);
+ }
+
+ @Test
+ public void testShouldLogShutdownLongPress() {
+ GlobalActionsDialogLite.ShutDownAction shutDownAction =
+ mGlobalActionsDialogLite.new ShutDownAction();
+ shutDownAction.onLongPress();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
+ }
+
+ @Test
+ public void testShouldLogRebootPress() {
+ GlobalActionsDialogLite.RestartAction restartAction =
+ mGlobalActionsDialogLite.new RestartAction();
+ restartAction.onPress();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_PRESS);
+ }
+
+ @Test
+ public void testShouldLogRebootLongPress() {
+ GlobalActionsDialogLite.RestartAction restartAction =
+ mGlobalActionsDialogLite.new RestartAction();
+ restartAction.onLongPress();
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index dd3a192..e62fa91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -289,6 +289,7 @@
captor.value.onLongClick(holder.player)
verify(mediaViewController, never()).openGuts()
+ verify(mediaViewController).closeGuts(false)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 406f40c..a974421 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -73,11 +74,15 @@
@Mock
private lateinit var mediaCarouselController: MediaCarouselController
@Mock
+ private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
+ @Mock
private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
+ @Captor
+ private lateinit var statusBarCallback: ArgumentCaptor<(StatusBarStateController.StateListener)>
@JvmField
@Rule
val mockito = MockitoJUnit.rule()
@@ -96,10 +101,13 @@
wakefulnessLifecycle,
statusBarKeyguardViewManager)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
+ verify(statusBarStateController).addCallback(statusBarCallback.capture())
setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN)
setupHost(qsHost, MediaHierarchyManager.LOCATION_QS)
setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS)
`when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+ `when`(mediaCarouselController.mediaCarouselScrollHandler)
+ .thenReturn(mediaCarouselScrollHandler)
// We'll use the viewmanager to verify a few calls below, let's reset this.
clearInvocations(mediaCarouselController)
}
@@ -153,4 +161,11 @@
verify(mediaCarouselController).closeGuts()
}
+
+ @Test
+ fun testCloseGutsWhenDoze() {
+ statusBarCallback.value.onDozingChanged(true)
+
+ verify(mediaCarouselController).closeGuts()
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 5c70a4ef2..7172307 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.people;
import static android.app.Notification.CATEGORY_MISSED_CALL;
+import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
@@ -25,6 +26,7 @@
import static android.app.people.PeopleSpaceTile.SHOW_CONTACTS;
import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS;
import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT;
@@ -127,6 +129,9 @@
.build();
@Mock
+ private Icon mIcon;
+
+ @Mock
private Context mMockContext;
@Mock
private PackageManager mPackageManager;
@@ -169,7 +174,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
PERSON_TILE_WITHOUT_NOTIFICATION, mOptions).getViews();
@@ -218,7 +223,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithLastInteraction, mOptions).getViews();
@@ -278,7 +283,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithAvailabilityAndNewStory, mOptions).getViews();
@@ -340,7 +345,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusTemplate, mOptions).getViews();
@@ -376,6 +381,8 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.image).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -403,7 +410,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusTemplate, mOptions).getViews();
@@ -413,6 +420,8 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.image).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -426,6 +435,55 @@
}
@Test
+ public void testCreateRemoteViewsWithStatusTemplateWithImageOnMediumAndLarge() {
+ PeopleSpaceTile tileWithIconInStatusTemplate =
+ PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
+ Arrays.asList(new ConversationStatus.Builder(PERSON_TILE.getId(),
+ ACTIVITY_ANNIVERSARY).setDescription("Anniversary").setAvailability(
+ AVAILABILITY_AVAILABLE).setIcon(mIcon).build())).build();
+ RemoteViews views = getPeopleTileViewHelper(
+ tileWithIconInStatusTemplate, mOptions).getViews();
+ View result = views.apply(mContext, null);
+
+ assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.VISIBLE, result.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.image).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // Has status.
+ TextView statusContent = (TextView) result.findViewById(R.id.name);
+ assertEquals(statusContent.getText(), "Anniversary");
+ assertThat(statusContent.getMaxLines()).isEqualTo(1);
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_large));
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
+ getSizeInDp(R.dimen.required_height_for_large));
+ RemoteViews largeView = getPeopleTileViewHelper(
+ tileWithIconInStatusTemplate, mOptions).getViews();
+ View largeResult = largeView.apply(mContext, null);
+
+ assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.image).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ View personIcon = largeResult.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
+ // Has status content.
+ statusContent = (TextView) largeResult.findViewById(R.id.text_content);
+ assertEquals(View.VISIBLE, statusContent.getVisibility());
+ assertEquals(statusContent.getText(), "Anniversary");
+ assertThat(statusContent.getMaxLines()).isEqualTo(2);
+ }
+
+ @Test
public void testCreateRemoteViewsWithPackageSuspended() {
PeopleSpaceTile tile = PERSON_TILE.toBuilder()
.setIsPackageSuspended(true)
@@ -434,7 +492,7 @@
tile, mOptions).getViews();
View result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
}
@Test
@@ -446,7 +504,7 @@
tile, mOptions).getViews();
View result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_work_profile_quiet_layout);
}
@Test
@@ -458,7 +516,7 @@
tileWithDndBlocking, mOptions).getViews();
View result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(BLOCK_CONVERSATIONS)
@@ -468,7 +526,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS)
@@ -477,7 +535,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS)
@@ -487,7 +545,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_STARRED_CONTACTS)
@@ -497,7 +555,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_STARRED_CONTACTS)
@@ -507,7 +565,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
@@ -517,7 +575,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
@@ -527,7 +585,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
@@ -536,7 +594,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
}
@Test
@@ -581,7 +639,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithMissedCallNotification, mOptions).getViews();
@@ -617,6 +675,7 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
assertEquals(View.GONE, result.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.scrim_layout).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -649,7 +708,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusAndNotification, mOptions).getViews();
@@ -659,6 +718,7 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
assertEquals(View.GONE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -725,7 +785,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusAndNotification, mOptions).getViews();
@@ -802,7 +862,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusAndNotification, mOptions).getViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 107ce28..d63c529 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -1041,7 +1041,6 @@
Bundle newOptions = new Bundle();
mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions);
-
// Check that options is not modified
verify(mAppWidgetManager, never()).updateAppWidgetOptions(
eq(SECOND_WIDGET_ID_WITH_SHORTCUT), any());
@@ -1478,6 +1477,8 @@
widgetSp5.edit().clear().commit();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
sp.edit().clear().commit();
+ mManager.mListeners.clear();
+ mManager.mTiles.clear();
}
private void setStorageForTile(String shortcutId, String packageName, int widgetId,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index d35597f..6f7bf3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -35,6 +36,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -82,6 +84,7 @@
private QuickQSPanelController mQuickQSPanelController;
private FakeTunerService mFakeTunerService;
private MetricsLogger mMetricsLogger = new FakeMetricsLogger();
+ private FalsingManagerFake mFalsingManager;
@Mock
private SettingsButton mSettingsButton;
@@ -95,12 +98,15 @@
private View mPowerMenuLiteView;
@Mock
private GlobalActionsDialogLite mGlobalActionsDialog;
+ @Mock
+ private UiEventLogger mUiEventLogger;
private QSFooterViewController mController;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ mFalsingManager = new FalsingManagerFake();
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
@@ -121,7 +127,8 @@
mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
- mMetricsLogger, new FalsingManagerFake(), false, mGlobalActionsDialog);
+ mMetricsLogger, mFalsingManager, false, mGlobalActionsDialog,
+ mUiEventLogger);
mController.init();
}
@@ -154,4 +161,27 @@
// Verify Settings wasn't launched.
verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
}
+
+ @Test
+ public void testLogPowerMenuClick() {
+ // Enable power menu button
+ mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
+ mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
+ mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
+ mMetricsLogger, new FalsingManagerFake(), true, mGlobalActionsDialog,
+ mUiEventLogger);
+ mController.init();
+ mController.setExpanded(true);
+ mFalsingManager.setFalseTap(false);
+
+ ArgumentCaptor<View.OnClickListener> onClickCaptor =
+ ArgumentCaptor.forClass(View.OnClickListener.class);
+ verify(mPowerMenuLiteView).setOnClickListener(onClickCaptor.capture());
+
+ onClickCaptor.getValue().onClick(mPowerMenuLiteView);
+
+ // Verify clicks are logged
+ verify(mUiEventLogger, times(1))
+ .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index e5e2e53..126dca5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -20,6 +20,7 @@
import android.graphics.drawable.Drawable
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
import android.text.TextUtils
import android.view.View
import androidx.test.filters.SmallTest
@@ -36,6 +37,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class QSTileViewImplTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 7c8b413..88852f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -74,6 +74,7 @@
private HeadsUpManager mHeadsUpManager;
@Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private RowContentBindStage mBindStage;
+ @Mock PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@Captor private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
private NotificationEntryListener mNotificationEntryListener;
private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>();
@@ -91,7 +92,7 @@
mGroupManager = new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
- () -> mock(PeopleNotificationIdentifier.class),
+ () -> mPeopleNotificationIdentifier,
Optional.of(mock(Bubbles.class)));
mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
@@ -107,15 +108,31 @@
mHeadsUpManager.addListener(mGroupAlertTransferHelper);
}
+ private void mockHasHeadsUpContentView(NotificationEntry entry,
+ boolean hasHeadsUpContentView) {
+ RowContentBindParams params = new RowContentBindParams();
+ if (hasHeadsUpContentView) {
+ params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
+ }
+ when(mBindStage.getStageParams(eq(entry))).thenReturn(params);
+ }
+
+ private void mockHasHeadsUpContentView(NotificationEntry entry) {
+ mockHasHeadsUpContentView(entry, true);
+ }
+
+ private void mockIsPriority(NotificationEntry priorityEntry) {
+ when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
+ .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
+ }
+
@Test
public void testSuppressedSummaryHeadsUpTransfersToChild() {
NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- RowContentBindParams params = new RowContentBindParams();
- params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry);
// Summary will be suppressed because there is only one child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -180,8 +197,7 @@
NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry, false);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -198,8 +214,7 @@
NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry, false);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -250,8 +265,7 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry, false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
@@ -270,8 +284,7 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry, false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
@@ -294,8 +307,7 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY, 47);
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+ mockHasHeadsUpContentView(childEntry, false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
@@ -311,4 +323,160 @@
assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
}
+
+ @Test
+ public void testOverriddenSummaryHeadsUpTransfersToPriority() {
+ // Creation order is oldest to newest, meaning the priority will be deemed newest
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ mockIsPriority(priorityEntry);
+
+ // summary gets heads up
+ mHeadsUpManager.showNotification(summaryEntry);
+
+ mockHasHeadsUpContentView(summaryEntry);
+ mockHasHeadsUpContentView(priorityEntry);
+ mockHasHeadsUpContentView(childEntry);
+
+ // Summary will have an alertOverride.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(priorityEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // An overridden summary should transfer its alert state to the priority.
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+ }
+
+ @Test
+ public void testOverriddenSummaryHeadsUpTransferDoesNotAlertPriorityIfUninflated() {
+ // Creation order is oldest to newest, meaning the priority will be deemed newest
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ mockIsPriority(priorityEntry);
+
+ // summary gets heads up
+ mHeadsUpManager.showNotification(summaryEntry);
+
+ mockHasHeadsUpContentView(summaryEntry);
+ mockHasHeadsUpContentView(priorityEntry, false);
+ mockHasHeadsUpContentView(childEntry);
+
+ // Summary will have an alertOverride.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(priorityEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Alert is immediately removed from summary, but we do not show priority yet either as its
+ // content is not inflated.
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+ assertTrue(mGroupAlertTransferHelper.isAlertTransferPending(priorityEntry));
+ }
+
+ @Test
+ public void testOverriddenSummaryHeadsUpTransfersToPriorityButBackAgain() {
+ // Creation order is oldest to newest, meaning the child2 will ultimately be deemed newest
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry childEntry2 = mGroupTestHelper.createChildNotification(groupAlert);
+ mockIsPriority(priorityEntry);
+
+ // summary gets heads up
+ mHeadsUpManager.showNotification(summaryEntry);
+
+ mockHasHeadsUpContentView(summaryEntry);
+ mockHasHeadsUpContentView(priorityEntry);
+ mockHasHeadsUpContentView(childEntry);
+ mockHasHeadsUpContentView(childEntry2);
+
+ // Summary will have an alertOverride.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(priorityEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // An overridden summary should transfer its alert state to the priority.
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+
+ mGroupManager.onEntryAdded(childEntry2);
+
+ // An overridden summary should transfer its alert state to the priority.
+ assertTrue(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry2.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+ }
+
+ @Test
+ public void testOverriddenSuppressedSummaryHeadsUpTransfersToChildThenToPriority() {
+ // Creation order is oldest to newest, meaning the priority will ultimately be deemed newest
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ mockIsPriority(priorityEntry);
+
+ // summary gets heads up
+ mHeadsUpManager.showNotification(summaryEntry);
+
+ mockHasHeadsUpContentView(summaryEntry);
+ mockHasHeadsUpContentView(priorityEntry);
+ mockHasHeadsUpContentView(childEntry);
+
+ // Summary will be suppressed, and the child will receive the alert
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
+
+ // Alert should be transferred "back" from the child to the priority
+ mGroupManager.onEntryAdded(priorityEntry);
+
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+ }
+
+ @Test
+ public void testOverriddenSuppressedSummaryHeadsUpTransfersToPriorityThenToChild() {
+ // Creation order is oldest to newest, meaning the child will ultimately be deemed newest
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ mockIsPriority(priorityEntry);
+
+ // summary gets heads up
+ mHeadsUpManager.showNotification(summaryEntry);
+
+ mockHasHeadsUpContentView(summaryEntry);
+ mockHasHeadsUpContentView(priorityEntry);
+ mockHasHeadsUpContentView(childEntry);
+
+ // Summary will have alert override of the priority
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(priorityEntry);
+
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+
+ // Alert should be transferred "back" from the priority to the child (which is newer)
+ mGroupManager.onEntryAdded(childEntry);
+
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 3e9fd51..0110d7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -21,9 +21,12 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Notification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -33,6 +36,8 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.wm.shell.bubbles.Bubbles;
@@ -58,6 +63,7 @@
private final NotificationGroupTestHelper mGroupTestHelper =
new NotificationGroupTestHelper(mContext);
+ @Mock PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@Mock HeadsUpManager mHeadsUpManager;
@Before
@@ -69,7 +75,7 @@
private void initializeGroupManager() {
mGroupManager = new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
- () -> mock(PeopleNotificationIdentifier.class),
+ () -> mPeopleNotificationIdentifier,
Optional.of(mock(Bubbles.class)));
mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
@@ -153,4 +159,72 @@
assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry));
assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry));
}
+
+ @Test
+ public void testAlertOverrideWithSiblings_0() {
+ helpTestAlertOverrideWithSiblings(0);
+ }
+
+ @Test
+ public void testAlertOverrideWithSiblings_1() {
+ helpTestAlertOverrideWithSiblings(1);
+ }
+
+ @Test
+ public void testAlertOverrideWithSiblings_2() {
+ helpTestAlertOverrideWithSiblings(2);
+ }
+
+ /**
+ * This tests, for a group with a priority entry and the given number of siblings, that:
+ * 1) the priority entry is identified as the alertOverride for the group
+ * 2) the onAlertOverrideChanged method is called at that time
+ * 3) when the priority entry is removed, these are reversed
+ */
+ private void helpTestAlertOverrideWithSiblings(int numSiblings) {
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ // Create entries in an order so that the priority entry can be deemed the newest child.
+ NotificationEntry[] siblings = new NotificationEntry[numSiblings];
+ for (int i = 0; i < numSiblings; i++) {
+ siblings[i] = mGroupTestHelper.createChildNotification(groupAlert);
+ }
+ NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+
+ // The priority entry is an important conversation.
+ when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
+ .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
+
+ // Register a listener so we can verify that the event is sent.
+ OnGroupChangeListener groupChangeListener = mock(OnGroupChangeListener.class);
+ mGroupManager.registerGroupChangeListener(groupChangeListener);
+
+ // Add all the entries. The order here shouldn't matter.
+ mGroupManager.onEntryAdded(summaryEntry);
+ for (int i = 0; i < numSiblings; i++) {
+ mGroupManager.onEntryAdded(siblings[i]);
+ }
+ mGroupManager.onEntryAdded(priorityEntry);
+
+ // Verify that the summary group has the priority child as its alertOverride
+ NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
+ assertEquals(priorityEntry, summaryGroup.alertOverride);
+ verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+
+ // Verify that only the priority notification is isolated from the group
+ assertEquals(priorityEntry, mGroupManager.getGroupSummary(priorityEntry));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(priorityEntry));
+ // Verify that the siblings are NOT isolated from the group
+ for (int i = 0; i < numSiblings; i++) {
+ assertEquals(summaryEntry, mGroupManager.getGroupSummary(siblings[i]));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(siblings[i]));
+ }
+
+ // Remove the priority notification to validate that it is removed as the alertOverride
+ mGroupManager.onEntryRemoved(priorityEntry);
+
+ // verify that the alertOverride is removed when the priority notification is
+ assertNull(summaryGroup.alertOverride);
+ verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 4bac762..45761df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -89,6 +89,7 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -243,6 +244,8 @@
private QuickAccessWalletClient mQuickAccessWalletClient;
@Mock
private KeyguardMediaController mKeyguardMediaController;
+ @Mock
+ private PrivacyDotViewController mPrivacyDotViewController;
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -351,6 +354,7 @@
mFeatureFlags,
mQuickAccessWalletClient,
mKeyguardMediaController,
+ mPrivacyDotViewController,
new FakeExecutor(new FakeSystemClock()));
mNotificationPanelViewController.initDependencies(
mStatusBar,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 3d07eb1..e5b7e3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -20,7 +20,6 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
@@ -100,8 +99,6 @@
private Icon mActionIcon;
- private int mSingleLinePaddingHorizontal;
- private int mDoubleLinePaddingHorizontal;
private int mSpacing;
private NotificationEntry mEntry;
@@ -141,10 +138,6 @@
mView = SmartReplyView.inflate(mContext, mConstants);
final Resources res = mContext.getResources();
- mSingleLinePaddingHorizontal = res.getDimensionPixelSize(
- R.dimen.smart_reply_button_padding_horizontal_single_line);
- mDoubleLinePaddingHorizontal = res.getDimensionPixelSize(
- R.dimen.smart_reply_button_padding_horizontal_double_line);
mSpacing = res.getDimensionPixelSize(R.dimen.smart_reply_button_spacing);
mNotification = new Notification.Builder(mContext, "")
@@ -588,18 +581,6 @@
layout.setBaselineAligned(false);
final boolean isRtl = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
- final int paddingHorizontal;
- switch (lineCount) {
- case 1:
- paddingHorizontal = mSingleLinePaddingHorizontal;
- break;
- case 2:
- paddingHorizontal = mDoubleLinePaddingHorizontal;
- break;
- default:
- fail("Invalid line count " + lineCount);
- return null;
- }
// Add smart replies
Button previous = null;
@@ -617,8 +598,6 @@
true /* delayOnClickListener */))
.iterator()));
for (Button current : inflatedReplies) {
- current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
- current.getPaddingBottom());
if (previous != null) {
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) previous.getLayoutParams();
@@ -647,8 +626,6 @@
// Add smart actions
for (Button current : inflatedSmartActions) {
- current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
- current.getPaddingBottom());
if (previous != null) {
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) previous.getLayoutParams();
diff --git a/packages/services/CameraExtensionsProxy/AndroidManifest.xml b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
index 7416cba..e5f460e 100644
--- a/packages/services/CameraExtensionsProxy/AndroidManifest.xml
+++ b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
@@ -2,8 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.camera">
- <uses-permission android:name="android.permission.CAMERA" />
-
<application
android:label="@string/app_name"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
index 0f05019..d44a417 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
@@ -25,19 +25,36 @@
import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.extension.CameraOutputConfig;
+import android.hardware.camera2.extension.CameraSessionConfig;
import android.hardware.camera2.extension.CaptureBundle;
+import android.hardware.camera2.extension.CaptureFailure;
import android.hardware.camera2.extension.CaptureStageImpl;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
+import android.hardware.camera2.extension.ICaptureCallback;
import android.hardware.camera2.extension.ICaptureProcessorImpl;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
+import android.hardware.camera2.extension.IRequestCallback;
+import android.hardware.camera2.extension.IRequestProcessorImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
-import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
+import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.ISessionProcessorImpl;
+import android.hardware.camera2.extension.LatencyRange;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.OutputSurface;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.extension.ParcelImage;
+import android.hardware.camera2.extension.ParcelTotalCaptureResult;
+import android.hardware.camera2.extension.Request;
import android.hardware.camera2.extension.SizeList;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.HardwareBuffer;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
import android.media.Image;
import android.media.ImageReader;
import android.os.ConditionVariable;
@@ -45,9 +62,11 @@
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
+import android.util.Range;
import android.util.Size;
import android.view.Surface;
@@ -69,10 +88,26 @@
import androidx.camera.extensions.impl.PreviewExtenderImpl.ProcessorType;
import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
import androidx.camera.extensions.impl.RequestUpdateProcessorImpl;
+import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.BeautyAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.BokehAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl;
+import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl;
+import androidx.camera.extensions.impl.advanced.HdrAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.ImageProcessorImpl;
+import androidx.camera.extensions.impl.advanced.ImageReaderOutputConfigImpl;
+import androidx.camera.extensions.impl.advanced.MultiResolutionImageReaderOutputConfigImpl;
+import androidx.camera.extensions.impl.advanced.NightAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.OutputSurfaceImpl;
+import androidx.camera.extensions.impl.advanced.RequestProcessorImpl;
+import androidx.camera.extensions.impl.advanced.SessionProcessorImpl;
+import androidx.camera.extensions.impl.advanced.SurfaceOutputConfigImpl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -83,13 +118,20 @@
private static final String CAMERA_EXTENSION_VERSION_NAME =
"androidx.camera.extensions.impl.ExtensionVersionImpl";
- private static final String LATEST_VERSION = "1.1.0";
- private static final String[] SUPPORTED_VERSION_PREFIXES = {"1.1.", "1.0."};
+ private static final String LATEST_VERSION = "1.2.0";
+ private static final String LEGACY_VERSION_PREFIX = "1.1";
+ private static final String ADVANCED_VERSION_PREFIX = "1.2";
+ private static final String[] SUPPORTED_VERSION_PREFIXES = {ADVANCED_VERSION_PREFIX,
+ LEGACY_VERSION_PREFIX, "1.0."};
private static final boolean EXTENSIONS_PRESENT = checkForExtensions();
private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ?
(new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null;
- private static final boolean LATEST_VERSION_SUPPORTED =
- EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(SUPPORTED_VERSION_PREFIXES[0]);
+ private static final boolean LEGACY_VERSION_SUPPORTED =
+ EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(LEGACY_VERSION_PREFIX);
+ private static final boolean ADVANCED_VERSION_SUPPORTED =
+ EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX);
+
+ private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>();
private static boolean checkForExtensions() {
try {
@@ -223,7 +265,7 @@
public long registerClient(Context ctx) {
synchronized (mLock) {
- if (LATEST_VERSION_SUPPORTED) {
+ if (LEGACY_VERSION_SUPPORTED) {
if (mActiveClients.isEmpty()) {
InitializerFuture status = new InitializerFuture();
InitializerImpl.init(EXTENSIONS_VERSION, ctx, new InitializeHandler(status),
@@ -257,7 +299,7 @@
public void unregisterClient(long clientId) {
synchronized (mLock) {
if (mActiveClients.remove(clientId) && mActiveClients.isEmpty() &&
- LATEST_VERSION_SUPPORTED) {
+ LEGACY_VERSION_SUPPORTED) {
InitializerFuture status = new InitializerFuture();
InitializerImpl.deinit(new ReleaseHandler(status),
new HandlerExecutor(mHandler));
@@ -321,17 +363,39 @@
}
}
+ /**
+ * @hide
+ */
+ public static AdvancedExtenderImpl initializeAdvancedExtensionImpl(int extensionType) {
+ switch (extensionType) {
+ case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC:
+ return new AutoAdvancedExtenderImpl();
+ case CameraExtensionCharacteristics.EXTENSION_BEAUTY:
+ return new BeautyAdvancedExtenderImpl();
+ case CameraExtensionCharacteristics.EXTENSION_BOKEH:
+ return new BokehAdvancedExtenderImpl();
+ case CameraExtensionCharacteristics.EXTENSION_HDR:
+ return new HdrAdvancedExtenderImpl();
+ case CameraExtensionCharacteristics.EXTENSION_NIGHT:
+ return new NightAdvancedExtenderImpl();
+ default:
+ throw new IllegalArgumentException("Unknown extension: " + extensionType);
+ }
+ }
+
@Override
public void onCreate() {
super.onCreate();
// This will setup the camera vendor tag descriptor in the service process
+ // along with all camera characteristics.
try {
CameraManager manager = getSystemService(CameraManager.class);
String [] cameraIds = manager.getCameraIdListNoLazy();
if (cameraIds != null) {
for (String cameraId : cameraIds) {
- CameraCharacteristics ch = manager.getCameraCharacteristics(cameraId);
+ mCharacteristicsHashMap.put(cameraId,
+ manager.getCameraCharacteristics(cameraId));
}
}
} catch (CameraAccessException e) {
@@ -372,6 +436,29 @@
return ret;
}
+ private static List<SizeList> initializeParcelable(
+ Map<Integer, List<android.util.Size>> sizes) {
+ if (sizes == null) {
+ return null;
+ }
+ ArrayList<SizeList> ret = new ArrayList<>(sizes.size());
+ for (Map.Entry<Integer, List<android.util.Size>> entry : sizes.entrySet()) {
+ SizeList sizeList = new SizeList();
+ sizeList.format = entry.getKey();
+ sizeList.sizes = new ArrayList<>();
+ for (android.util.Size size : entry.getValue()) {
+ android.hardware.camera2.extension.Size sz =
+ new android.hardware.camera2.extension.Size();
+ sz.width = size.getWidth();
+ sz.height = size.getHeight();
+ sizeList.sizes.add(sz);
+ }
+ ret.add(sizeList);
+ }
+
+ return ret;
+ }
+
private static CameraMetadataNative initializeParcelableMetadata(
List<Pair<CaptureRequest.Key, Object>> paramList) {
if (paramList == null) {
@@ -386,6 +473,20 @@
return ret;
}
+ private static CameraMetadataNative initializeParcelableMetadata(
+ Map<CaptureRequest.Key<?>, Object> paramMap) {
+ if (paramMap == null) {
+ return null;
+ }
+
+ CameraMetadataNative ret = new CameraMetadataNative();
+ for (Map.Entry<CaptureRequest.Key<?>, Object> param : paramMap.entrySet()) {
+ ret.set(((CaptureRequest.Key) param.getKey()), param.getValue());
+ }
+
+ return ret;
+ }
+
private static android.hardware.camera2.extension.CaptureStageImpl initializeParcelable(
androidx.camera.extensions.impl.CaptureStageImpl captureStage) {
if (captureStage == null) {
@@ -400,6 +501,20 @@
return ret;
}
+ private Request initializeParcelable(RequestProcessorImpl.Request request, int requestId) {
+ Request ret = new Request();
+ ret.targetOutputConfigIds = new ArrayList<>();
+ for (int id : request.getTargetOutputConfigIds()) {
+ OutputConfigId configId = new OutputConfigId();
+ configId.id = id;
+ ret.targetOutputConfigIds.add(configId);
+ }
+ ret.templateId = request.getTemplateId();
+ ret.parameters = initializeParcelableMetadata(request.getParameters());
+ ret.requestId = requestId;
+ return ret;
+ }
+
private class CameraExtensionsProxyServiceStub extends ICameraExtensionsProxyService.Stub {
@Override
public long registerClient() {
@@ -412,6 +527,23 @@
}
@Override
+ public boolean advancedExtensionsSupported() {
+ return ADVANCED_VERSION_SUPPORTED;
+ }
+
+ @Override
+ public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType) {
+ AdvancedExtenderImpl extension;
+ try {
+ extension = initializeAdvancedExtensionImpl(extensionType);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+
+ return new AdvancedExtenderImplStub(extension);
+ }
+
+ @Override
public IPreviewExtenderImpl initializePreviewExtension(int extensionType) {
Pair<PreviewExtenderImpl, ImageCaptureExtenderImpl> extension;
try {
@@ -436,6 +568,475 @@
}
}
+ private class AdvancedExtenderImplStub extends IAdvancedExtenderImpl.Stub {
+ private final AdvancedExtenderImpl mAdvancedExtender;
+
+ public AdvancedExtenderImplStub(AdvancedExtenderImpl advancedExtender) {
+ mAdvancedExtender = advancedExtender;
+ }
+
+ @Override
+ public boolean isExtensionAvailable(String cameraId) {
+ return mAdvancedExtender.isExtensionAvailable(cameraId, mCharacteristicsHashMap);
+ }
+
+ @Override
+ public void init(String cameraId) {
+ mAdvancedExtender.init(cameraId, mCharacteristicsHashMap);
+ }
+
+ @Override
+ public List<SizeList> getSupportedPreviewOutputResolutions(String cameraId) {
+ Map<Integer, List<Size>> supportedSizesMap =
+ mAdvancedExtender.getSupportedPreviewOutputResolutions(cameraId);
+ if (supportedSizesMap != null) {
+ return initializeParcelable(supportedSizesMap);
+ }
+
+ return null;
+ }
+
+ @Override
+ public List<SizeList> getSupportedCaptureOutputResolutions(String cameraId) {
+ Map<Integer, List<Size>> supportedSizesMap =
+ mAdvancedExtender.getSupportedCaptureOutputResolutions(cameraId);
+ if (supportedSizesMap != null) {
+ return initializeParcelable(supportedSizesMap);
+ }
+
+ return null;
+ }
+
+ @Override
+ public LatencyRange getEstimatedCaptureLatencyRange(String cameraId,
+ android.hardware.camera2.extension.Size outputSize, int format) {
+ Size sz = new Size(outputSize.width, outputSize.height);
+ Range<Long> latencyRange = mAdvancedExtender.getEstimatedCaptureLatencyRange(cameraId,
+ sz, format);
+ if (latencyRange != null) {
+ LatencyRange ret = new LatencyRange();
+ ret.min = latencyRange.getLower();
+ ret.max = latencyRange.getUpper();
+ return ret;
+ }
+
+ return null;
+ }
+
+ @Override
+ public ISessionProcessorImpl getSessionProcessor() {
+ return new SessionProcessorImplStub(mAdvancedExtender.createSessionProcessor());
+ }
+ }
+
+ private class CaptureCallbackStub implements SessionProcessorImpl.CaptureCallback {
+ private final ICaptureCallback mCaptureCallback;
+
+ private CaptureCallbackStub(ICaptureCallback captureCallback) {
+ mCaptureCallback = captureCallback;
+ }
+
+ @Override
+ public void onCaptureStarted(int captureSequenceId, long timestamp) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureStarted(captureSequenceId, timestamp);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture start due to remote " +
+ "exception!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureProcessStarted(int captureSequenceId) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureProcessStarted(captureSequenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture process start due to remote " +
+ "exception!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureFailed(int captureSequenceId) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureFailed(captureSequenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture failure due to remote " +
+ "exception!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int captureSequenceId) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureSequenceCompleted(captureSequenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture sequence end due to remote " +
+ "exception!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(int captureSequenceId) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureSequenceAborted(captureSequenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture sequence abort due to remote " +
+ "exception!");
+ }
+ }
+ }
+ }
+
+ private class RequestCallbackStub extends IRequestCallback.Stub {
+ private final List<RequestProcessorImpl.Request> mRequests;
+ private final RequestProcessorImpl.Callback mCallback;
+
+ public RequestCallbackStub(List<RequestProcessorImpl.Request> requests,
+ RequestProcessorImpl.Callback callback) {
+ mCallback = callback;
+ if (mCallback != null) {
+ mRequests = requests;
+ } else {
+ Log.w(TAG, "No valid request callbacks!");
+ mRequests = new ArrayList<>();
+ }
+ }
+
+ @Override
+ public void onCaptureStarted(int requestId, long frameNumber, long timestamp) {
+ if (mCallback != null) {
+ if (mRequests.get(requestId) != null) {
+ mCallback.onCaptureStarted(mRequests.get(requestId), frameNumber, timestamp);
+ } else {
+ Log.e(TAG,"Request id: " + requestId + " not found!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureProgressed(int requestId, ParcelCaptureResult partialResult) {
+ if (mCallback != null) {
+ if (mRequests.get(requestId) != null) {
+ CaptureResult result = new CaptureResult(partialResult.cameraId,
+ partialResult.results, partialResult.parent, partialResult.sequenceId,
+ partialResult.frameNumber);
+ mCallback.onCaptureProgressed(mRequests.get(requestId), result);
+ } else {
+ Log.e(TAG,"Request id: " + requestId + " not found!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureCompleted(int requestId, ParcelTotalCaptureResult totalCaptureResult) {
+ if (mCallback != null) {
+ if (mRequests.get(requestId) != null) {
+ PhysicalCaptureResultInfo[] physicalResults = new PhysicalCaptureResultInfo[0];
+ if ((totalCaptureResult.physicalResult != null) &&
+ (!totalCaptureResult.physicalResult.isEmpty())) {
+ int count = totalCaptureResult.physicalResult.size();
+ physicalResults = new PhysicalCaptureResultInfo[count];
+ physicalResults = totalCaptureResult.physicalResult.toArray(
+ physicalResults);
+ }
+ ArrayList<CaptureResult> partials = new ArrayList<>(
+ totalCaptureResult.partials.size());
+ for (ParcelCaptureResult parcelResult : totalCaptureResult.partials) {
+ partials.add(new CaptureResult(parcelResult.cameraId, parcelResult.results,
+ parcelResult.parent, parcelResult.sequenceId,
+ parcelResult.frameNumber));
+ }
+ TotalCaptureResult result = new TotalCaptureResult(
+ totalCaptureResult.logicalCameraId, totalCaptureResult.results,
+ totalCaptureResult.parent, totalCaptureResult.sequenceId,
+ totalCaptureResult.frameNumber, partials, totalCaptureResult.sessionId,
+ physicalResults);
+ mCallback.onCaptureCompleted(mRequests.get(requestId), result);
+ } else {
+ Log.e(TAG,"Request id: " + requestId + " not found!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureFailed(int requestId, CaptureFailure captureFailure) {
+ if (mCallback != null) {
+ if (mRequests.get(requestId) != null) {
+ android.hardware.camera2.CaptureFailure failure =
+ new android.hardware.camera2.CaptureFailure(captureFailure.request,
+ captureFailure.reason, captureFailure.dropped,
+ captureFailure.sequenceId, captureFailure.frameNumber,
+ captureFailure.errorPhysicalCameraId);
+ mCallback.onCaptureFailed(mRequests.get(requestId), failure);
+ } else {
+ Log.e(TAG,"Request id: " + requestId + " not found!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId) {
+ if (mCallback != null) {
+ if (mRequests.get(requestId) != null) {
+ mCallback.onCaptureBufferLost(mRequests.get(requestId), frameNumber,
+ outputStreamId);
+ } else {
+ Log.e(TAG,"Request id: " + requestId + " not found!");
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) {
+ if (mCallback != null) {
+ mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber);
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(int sequenceId) {
+ if (mCallback != null) {
+ mCallback.onCaptureSequenceAborted(sequenceId);
+ }
+ }
+ }
+
+ private class ImageProcessorImplStub extends IImageProcessorImpl.Stub {
+ private final ImageProcessorImpl mImageProcessor;
+
+ public ImageProcessorImplStub(ImageProcessorImpl imageProcessor) {
+ mImageProcessor = imageProcessor;
+ }
+
+ @Override
+ public void onNextImageAvailable(OutputConfigId outputConfigId, ParcelImage img) {
+ if (mImageProcessor != null) {
+ mImageProcessor.onNextImageAvailable(outputConfigId.id, img.timestamp,
+ new ImageReferenceImpl(img));
+ }
+ }
+ }
+
+ private class RequestProcessorStub implements RequestProcessorImpl {
+ private final IRequestProcessorImpl mRequestProcessor;
+
+ public RequestProcessorStub(IRequestProcessorImpl requestProcessor) {
+ mRequestProcessor = requestProcessor;
+ }
+
+ @Override
+ public void setImageProcessor(int outputConfigId,
+ ImageProcessorImpl imageProcessor) {
+ OutputConfigId configId = new OutputConfigId();
+ configId.id = outputConfigId;
+ try {
+ mRequestProcessor.setImageProcessor(configId,
+ new ImageProcessorImplStub(imageProcessor));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to set image processor due to remote exception!");
+ }
+ }
+
+ @Override
+ public boolean submit(Request request, Callback callback) {
+ ArrayList<Request> requests = new ArrayList<>();
+ requests.add(request);
+ return submit(requests, callback);
+ }
+
+ @Override
+ public boolean submit(List<Request> requests, Callback callback) {
+ ArrayList<android.hardware.camera2.extension.Request> captureRequests =
+ new ArrayList<>();
+ int requestId = 0;
+ for (Request request : requests) {
+ captureRequests.add(initializeParcelable(request, requestId));
+ requestId++;
+ }
+
+ try {
+ return mRequestProcessor.submitBurst(captureRequests,
+ new RequestCallbackStub(requests, callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to submit request due to remote exception!");
+ }
+ return false;
+ }
+
+ @Override
+ public boolean setRepeating(Request request, Callback callback) {
+ try {
+ ArrayList<Request> requests = new ArrayList<>();
+ requests.add(request);
+ return mRequestProcessor.setRepeating(initializeParcelable(request, 0),
+ new RequestCallbackStub(requests, callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to submit repeating request due to remote exception!");
+ }
+
+ return false;
+ }
+
+ @Override
+ public void abortCaptures() {
+ try {
+ mRequestProcessor.abortCaptures();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to abort requests due to remote exception!");
+ }
+ }
+
+ @Override
+ public void stopRepeating() {
+ try {
+ mRequestProcessor.stopRepeating();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to stop repeating request due to remote exception!");
+ }
+ }
+ }
+
+ private class SessionProcessorImplStub extends ISessionProcessorImpl.Stub {
+ private final SessionProcessorImpl mSessionProcessor;
+
+ public SessionProcessorImplStub(SessionProcessorImpl sessionProcessor) {
+ mSessionProcessor = sessionProcessor;
+ }
+
+ @Override
+ public CameraSessionConfig initSession(String cameraId, OutputSurface previewSurface,
+ OutputSurface burstSurface) {
+ OutputSurfaceImplStub outputPreviewSurfaceImpl =
+ new OutputSurfaceImplStub(previewSurface);
+ OutputSurfaceImplStub outputBurstSurfaceImpl =
+ new OutputSurfaceImplStub(burstSurface);
+
+ Camera2SessionConfigImpl sessionConfig = mSessionProcessor.initSession(cameraId,
+ mCharacteristicsHashMap, getApplicationContext(), outputPreviewSurfaceImpl,
+ outputBurstSurfaceImpl, null /*imageAnalysisSurfaceConfig*/);
+
+ List<Camera2OutputConfigImpl> outputConfigs = sessionConfig.getOutputConfigs();
+ CameraSessionConfig ret = new CameraSessionConfig();
+ ret.outputConfigs = new ArrayList<>();
+ for (Camera2OutputConfigImpl output : outputConfigs) {
+ CameraOutputConfig entry = new CameraOutputConfig();
+ entry.outputId = new OutputConfigId();
+ entry.outputId.id = output.getId();
+ entry.physicalCameraId = output.getPhysicalCameraId();
+ entry.surfaceGroupId = output.getSurfaceGroupId();
+ if (output instanceof SurfaceOutputConfigImpl) {
+ SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output;
+ entry.type = CameraOutputConfig.TYPE_SURFACE;
+ entry.surface = surfaceConfig.getSurface();
+ } else if (output instanceof ImageReaderOutputConfigImpl) {
+ ImageReaderOutputConfigImpl imageReaderOutputConfig =
+ (ImageReaderOutputConfigImpl) output;
+ entry.type = CameraOutputConfig.TYPE_IMAGEREADER;
+ entry.size = new android.hardware.camera2.extension.Size();
+ entry.size.width = imageReaderOutputConfig.getSize().getWidth();
+ entry.size.height = imageReaderOutputConfig.getSize().getHeight();
+ entry.imageFormat = imageReaderOutputConfig.getImageFormat();
+ entry.capacity = imageReaderOutputConfig.getMaxImages();
+ } else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) {
+ MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig =
+ (MultiResolutionImageReaderOutputConfigImpl) output;
+ entry.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER;
+ entry.imageFormat = multiResReaderConfig.getImageFormat();
+ entry.capacity = multiResReaderConfig.getMaxImages();
+ } else {
+ throw new IllegalStateException("Unknown output config type!");
+ }
+ List<Camera2OutputConfigImpl> sharedOutputs =
+ output.getSurfaceSharingOutputConfigs();
+ if ((sharedOutputs != null) && (!sharedOutputs.isEmpty())) {
+ entry.surfaceSharingOutputConfigs = new ArrayList<>();
+ for (Camera2OutputConfigImpl sharedOutput : sharedOutputs) {
+ OutputConfigId outputId = new OutputConfigId();
+ outputId.id = sharedOutput.getId();
+ entry.surfaceSharingOutputConfigs.add(outputId);
+ }
+ }
+ ret.outputConfigs.add(entry);
+ }
+ ret.sessionTemplateId = sessionConfig.getSessionTemplateId();
+ ret.sessionParameter = initializeParcelableMetadata(
+ sessionConfig.getSessionParameters());
+
+ return ret;
+ }
+
+ @Override
+ public void deInitSession() {
+ mSessionProcessor.deInitSession();
+ }
+
+ @Override
+ public void onCaptureSessionStart(IRequestProcessorImpl requestProcessor) {
+ mSessionProcessor.onCaptureSessionStart(new RequestProcessorStub(requestProcessor));
+ }
+
+ @Override
+ public void onCaptureSessionEnd() {
+ mSessionProcessor.onCaptureSessionEnd();
+ }
+
+ @Override
+ public int startRepeating(ICaptureCallback callback) {
+ return mSessionProcessor.startRepeating(new CaptureCallbackStub(callback));
+ }
+
+ @Override
+ public void stopRepeating() {
+ mSessionProcessor.stopRepeating();
+ }
+
+ @Override
+ public int startCapture(ICaptureCallback callback, int jpegRotation, int jpegQuality) {
+ HashMap<CaptureRequest.Key<?>, Object> paramMap = new HashMap<>();
+ paramMap.put(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
+ paramMap.put(CaptureRequest.JPEG_QUALITY, jpegQuality);
+ mSessionProcessor.setParameters(paramMap);
+ return mSessionProcessor.startCapture(new CaptureCallbackStub(callback));
+ }
+ }
+
+ private class OutputSurfaceImplStub implements OutputSurfaceImpl {
+ private final Surface mSurface;
+ private final Size mSize;
+ private final int mImageFormat;
+
+ public OutputSurfaceImplStub(OutputSurface outputSurface) {
+ mSurface = outputSurface.surface;
+ mSize = new Size(outputSurface.size.width, outputSurface.size.height);
+ mImageFormat = outputSurface.imageFormat;
+ }
+
+ @Override
+ public Surface getSurface() {
+ return mSurface;
+ }
+
+ @Override
+ public Size getSize() {
+ return mSize;
+ }
+
+ @Override
+ public int getImageFormat() {
+ return mImageFormat;
+ }
+ }
+
private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub {
private final PreviewExtenderImpl mPreviewExtender;
@@ -471,7 +1072,7 @@
@Override
public void init(String cameraId, CameraMetadataNative chars) {
- if (LATEST_VERSION_SUPPORTED) {
+ if (LEGACY_VERSION_SUPPORTED) {
mPreviewExtender.init(cameraId, new CameraCharacteristics(chars));
}
}
@@ -535,7 +1136,7 @@
@Override
public List<SizeList> getSupportedResolutions() {
- if (LATEST_VERSION_SUPPORTED) {
+ if (LEGACY_VERSION_SUPPORTED) {
List<Pair<Integer, android.util.Size[]>> sizes =
mPreviewExtender.getSupportedResolutions();
if ((sizes != null) && !sizes.isEmpty()) {
@@ -581,7 +1182,7 @@
@Override
public void init(String cameraId, CameraMetadataNative chars) {
- if (LATEST_VERSION_SUPPORTED) {
+ if (LEGACY_VERSION_SUPPORTED) {
mImageExtender.init(cameraId, new CameraCharacteristics(chars));
}
}
@@ -627,7 +1228,7 @@
@Override
public List<SizeList> getSupportedResolutions() {
- if (LATEST_VERSION_SUPPORTED) {
+ if (LEGACY_VERSION_SUPPORTED) {
List<Pair<Integer, android.util.Size[]>> sizes =
mImageExtender.getSupportedResolutions();
if ((sizes != null) && !sizes.isEmpty()) {
@@ -737,6 +1338,51 @@
}
}
+ private class ImageReferenceImpl extends ExtensionImage
+ implements androidx.camera.extensions.impl.advanced.ImageReferenceImpl {
+
+ private final Object mImageLock = new Object();
+ private int mReferenceCount;
+
+ private ImageReferenceImpl(ParcelImage parcelImage) {
+ super(parcelImage);
+ mReferenceCount = 1;
+ }
+
+ @Override
+ public boolean increment() {
+ synchronized (mImageLock) {
+ if (mReferenceCount <= 0) {
+ return false;
+ }
+ mReferenceCount++;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean decrement() {
+ synchronized (mImageLock) {
+ if (mReferenceCount <= 0) {
+ return false;
+ }
+ mReferenceCount--;
+
+ if (mReferenceCount <= 0) {
+ close();
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public Image get() {
+ return this;
+ }
+ }
+
private class ExtensionImage extends android.media.Image {
private final android.hardware.camera2.extension.ParcelImage mParcelImage;
private GraphicBuffer mGraphicBuffer;
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index db36e62..c3543e7 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -361,10 +361,13 @@
try {
executeRescueLevelInternal(context, level, failedPackage);
EventLogTags.writeRescueSuccess(level);
- logCriticalInfo(Log.DEBUG,
- "Finished rescue level " + levelToString(level));
+ String successMsg = "Finished rescue level " + levelToString(level);
+ if (!TextUtils.isEmpty(failedPackage)) {
+ successMsg += " for package " + failedPackage;
+ }
+ logCriticalInfo(Log.DEBUG, successMsg);
} catch (Throwable t) {
- logRescueException(level, t);
+ logRescueException(level, failedPackage, t);
}
}
@@ -427,7 +430,7 @@
pm.reboot(TAG);
}
} catch (Throwable t) {
- logRescueException(level, t);
+ logRescueException(level, failedPackage, t);
}
};
thread = new Thread(runnable);
@@ -441,7 +444,7 @@
try {
RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
} catch (Throwable t) {
- logRescueException(level, t);
+ logRescueException(level, failedPackage, t);
}
}
};
@@ -455,11 +458,15 @@
}
}
- private static void logRescueException(int level, Throwable t) {
+ private static void logRescueException(int level, @Nullable String failedPackageName,
+ Throwable t) {
final String msg = ExceptionUtils.getCompleteMessage(t);
EventLogTags.writeRescueFailure(level, msg);
- logCriticalInfo(Log.ERROR,
- "Failed rescue level " + levelToString(level) + ": " + msg);
+ String failureMsg = "Failed rescue level " + levelToString(level);
+ if (!TextUtils.isEmpty(failedPackageName)) {
+ failureMsg += " for package " + failedPackageName;
+ }
+ logCriticalInfo(Log.ERROR, failureMsg + ": " + msg);
}
private static int mapRescueLevelToUserImpact(int rescueLevel) {
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 7763ad9..a05eb68 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -469,7 +469,7 @@
@Override
public void setIndividualSensorPrivacy(@UserIdInt int userId, int sensor, boolean enable) {
enforceManageSensorPrivacyPermission();
- if (!canChangeIndividualSensorPrivacy(sensor)) {
+ if (!canChangeIndividualSensorPrivacy(userId, sensor)) {
return;
}
@@ -500,14 +500,14 @@
mHandler.onSensorPrivacyChanged(userId, sensor, enable);
}
- private boolean canChangeIndividualSensorPrivacy(int sensor) {
+ private boolean canChangeIndividualSensorPrivacy(@UserIdInt int userId, int sensor) {
if (sensor == MICROPHONE && mEmergencyCallHelper.isInEmergencyCall()) {
// During emergency call the microphone toggle managed automatically
Log.i(TAG, "Can't change mic toggle during an emergency call");
return false;
}
- if (mKeyguardManager != null && mKeyguardManager.isDeviceLocked()) {
+ if (mKeyguardManager != null && mKeyguardManager.isDeviceLocked(userId)) {
Log.i(TAG, "Can't change mic/cam toggle while device is locked");
return false;
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 9835a9a..7a0a3a7 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -97,6 +97,7 @@
"/system/bin/audioserver",
"/system/bin/cameraserver",
"/system/bin/drmserver",
+ "/system/bin/keystore2",
"/system/bin/mediadrmserver",
"/system/bin/mediaserver",
"/system/bin/netd",
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 70538bb..83cff15 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9335,7 +9335,7 @@
pw.println(" mFgsStartTempAllowList:");
final long currentTimeNow = System.currentTimeMillis();
final long elapsedRealtimeNow = SystemClock.elapsedRealtime();
- final Set<Integer> uids = mFgsStartTempAllowList.keySet();
+ final Set<Integer> uids = new ArraySet<>(mFgsStartTempAllowList.keySet());
for (Integer uid : uids) {
final Pair<Long, FgsTempAllowListItem> entry = mFgsStartTempAllowList.get(uid);
if (entry == null) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index f0b116c..de2c11b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -828,6 +828,7 @@
} else {
r.receiverTime = SystemClock.uptimeMillis();
maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+ maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
@@ -911,9 +912,16 @@
return false;
}
- final void scheduleTempAllowlistLocked(int uid, long duration, BroadcastRecord r,
- @TempAllowListType int type, @ReasonCode int reasonCode,
- @Nullable String reason) {
+ void maybeScheduleTempAllowlistLocked(int uid, BroadcastRecord r,
+ @Nullable BroadcastOptions brOptions) {
+ if (brOptions == null || brOptions.getTemporaryAppAllowlistDuration() <= 0) {
+ return;
+ }
+ long duration = brOptions.getTemporaryAppAllowlistDuration();
+ final @TempAllowListType int type = brOptions.getTemporaryAppAllowlistType();
+ final @ReasonCode int reasonCode = brOptions.getTemporaryAppAllowlistReasonCode();
+ final String reason = brOptions.getTemporaryAppAllowlistReason();
+
if (duration > Integer.MAX_VALUE) {
duration = Integer.MAX_VALUE;
}
@@ -1344,13 +1352,6 @@
// r is guaranteed ordered at this point, so we know finishReceiverLocked()
// will get a callback and handle the activity start token lifecycle.
}
- if (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0) {
- scheduleTempAllowlistLocked(filter.owningUid,
- brOptions.getTemporaryAppAllowlistDuration(), r,
- brOptions.getTemporaryAppAllowlistType(),
- brOptions.getTemporaryAppAllowlistReasonCode(),
- brOptions.getTemporaryAppAllowlistReason());
- }
}
return;
}
@@ -1646,16 +1647,9 @@
+ info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ receiverUid);
}
-
final boolean isActivityCapable =
(brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0);
- if (isActivityCapable) {
- scheduleTempAllowlistLocked(receiverUid,
- brOptions.getTemporaryAppAllowlistDuration(), r,
- brOptions.getTemporaryAppAllowlistType(),
- brOptions.getTemporaryAppAllowlistReasonCode(),
- brOptions.getTemporaryAppAllowlistReason());
- }
+ maybeScheduleTempAllowlistLocked(receiverUid, r, brOptions);
// Report that a component is used for explicit broadcasts.
if (!r.intent.isExcludingStopped() && r.curComponent != null
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index cc750ce..709139e 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -838,7 +838,7 @@
KEY_FREEZER_DEBOUNCE_TIMEOUT, DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
if (mFreezerDebounceTimeout < 0) {
- mFullDeltaRssThrottleKb = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
+ mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
}
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 40db086..01bbb03 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -394,6 +394,10 @@
mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
final int pid = msg.arg1;
final int group = msg.arg2;
+ if (pid == ActivityManagerService.MY_PID) {
+ // Skip setting the process group for system_server, keep it as default.
+ return true;
+ }
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup "
+ msg.obj + " to " + group);
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index a33e7e5..408c7cb 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -171,7 +171,7 @@
return mFreezeExempt;
}
- @GuardedBy("mPreLock")
+ @GuardedBy("mProcLock")
void setFreezeExempt(boolean exempt) {
mFreezeExempt = exempt;
}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 78ff67a..05c4431 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -296,9 +296,7 @@
FrameworkStatsLog.USER_LEVEL_HIBERNATION_STATE_CHANGED,
stateSnapshot.packageName,
userIdSnapshot,
- stateSnapshot.hibernated,
- // TODO(b/187224817): This isn't the expected value right now.
- stateSnapshot.lastUnhibernatedMs);
+ stateSnapshot.hibernated);
});
List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 098ce7c..a2722cb 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6535,6 +6535,10 @@
if (index == -1) {
continue;
}
+ if (mPublicStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED
+ && mCameraSoundForced) {
+ index = mIndexMax;
+ }
if (DEBUG_VOL) {
Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
+ " for group " + mAudioVolumeGroup.name() + ", device: " + name
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 677ea5d..6482a2e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -46,6 +46,7 @@
* Interface that ClientMonitor holders should use to receive callbacks.
*/
public interface Callback {
+
/**
* Invoked when the ClientMonitor operation has been started (e.g. reached the head of
* the queue and becomes the current operation).
@@ -222,6 +223,7 @@
+ this.getClass().getSimpleName()
+ ", " + getProtoEnum()
+ ", " + getOwnerString()
- + ", " + getCookie() + "}";
+ + ", " + getCookie()
+ + ", " + getTargetUserId() + "}";
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index 62a9769..f1c786b49 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -132,11 +132,13 @@
}
}
- public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
+ /** Called when a challenged has been generated. */
+ public void onChallengeGenerated(int sensorId, int userId, long challenge)
+ throws RemoteException {
if (mFaceServiceReceiver != null) {
- mFaceServiceReceiver.onChallengeGenerated(sensorId, challenge);
+ mFaceServiceReceiver.onChallengeGenerated(sensorId, userId, challenge);
} else if (mFingerprintServiceReceiver != null) {
- mFingerprintServiceReceiver.onChallengeGenerated(sensorId, challenge);
+ mFingerprintServiceReceiver.onChallengeGenerated(sensorId, userId, challenge);
}
}
@@ -153,18 +155,6 @@
}
}
- public void onChallengeInterrupted(int sensorId) throws RemoteException {
- if (mFaceServiceReceiver != null) {
- mFaceServiceReceiver.onChallengeInterrupted(sensorId);
- }
- }
-
- public void onChallengeInterruptFinished(int sensorId) throws RemoteException {
- if (mFaceServiceReceiver != null) {
- mFaceServiceReceiver.onChallengeInterruptFinished(sensorId);
- }
- }
-
// Fingerprint-specific callbacks for FingerprintManager only
public void onUdfpsPointerDown(int sensorId) throws RemoteException {
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 1fcad62..3d74f36 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -40,7 +40,7 @@
@Override
public void unableToStart() {
try {
- getListener().onChallengeGenerated(getSensorId(), 0L);
+ getListener().onChallengeGenerated(getSensorId(), getTargetUserId(), 0L);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send error", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index 8726923..57c1c74 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -110,17 +110,7 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) {
-
- }
-
- @Override
- public void onChallengeInterrupted(int sensorId) {
-
- }
-
- @Override
- public void onChallengeInterruptFinished(int sensorId) {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index 904c399..d76036b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -52,7 +52,7 @@
void onChallengeGenerated(int sensorId, int userId, long challenge) {
try {
- getListener().onChallengeGenerated(sensorId, challenge);
+ getListener().onChallengeGenerated(sensorId, userId, challenge);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send challenge", e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index f806767..d0580c7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -99,17 +99,7 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) {
-
- }
-
- @Override
- public void onChallengeInterrupted(int sensorId) {
-
- }
-
- @Override
- public void onChallengeInterruptFinished(int sensorId) {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index f908fba..a5bb0f4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -80,6 +80,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.time.Clock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -93,7 +94,13 @@
public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
private static final String TAG = "Face10";
+
private static final int ENROLL_TIMEOUT_SEC = 75;
+ private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60 * 1000;
+ private static final int GENERATE_CHALLENGE_COUNTER_TTL_MILLIS =
+ FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC * 1000;
+ @VisibleForTesting
+ public static Clock sSystemClock = Clock.systemUTC();
private boolean mTestHalEnabled;
@@ -102,19 +109,15 @@
@NonNull private final BiometricScheduler mScheduler;
@NonNull private final Handler mHandler;
@NonNull private final HalClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon;
- @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
@NonNull private final LockoutHalImpl mLockoutTracker;
@NonNull private final UsageStats mUsageStats;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
@Nullable private IBiometricsFace mDaemon;
@NonNull private final HalResultController mHalResultController;
- // If a challenge is generated, keep track of its owner. Since IBiometricsFace@1.0 only
- // supports a single in-flight challenge, we must notify the interrupted owner that its
- // challenge is no longer valid. The interrupted owner will be notified when the interrupter
- // has finished.
- @Nullable private FaceGenerateChallengeClient mCurrentChallengeOwner;
private int mCurrentUserId = UserHandle.USER_NULL;
private final int mSensorId;
+ private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
+ private FaceGenerateChallengeClient mGeneratedChallengeCache = null;
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
@@ -335,7 +338,6 @@
mAuthenticatorIds = new HashMap<>();
mLazyDaemon = Face10.this::getDaemon;
mLockoutTracker = new LockoutHalImpl();
- mLockoutResetDispatcher = lockoutResetDispatcher;
mHalResultController = new HalResultController(sensorProps.sensorId, context, mHandler,
mScheduler, mLockoutTracker, lockoutResetDispatcher);
mHalResultController.setCallback(() -> {
@@ -480,56 +482,56 @@
return getDaemon() != null;
}
+ private boolean isGeneratedChallengeCacheValid() {
+ return mGeneratedChallengeCache != null
+ && sSystemClock.millis() - mGeneratedChallengeCache.getCreatedAt()
+ < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS;
+ }
+
+ private void incrementChallengeCount() {
+ mGeneratedChallengeCount.add(0, sSystemClock.millis());
+ }
+
+ private int decrementChallengeCount() {
+ final long now = sSystemClock.millis();
+ // ignore values that are old in case generate/revoke calls are not matched
+ // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing
+ mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS);
+ if (!mGeneratedChallengeCount.isEmpty()) {
+ mGeneratedChallengeCount.remove(0);
+ }
+ return mGeneratedChallengeCount.size();
+ }
+
/**
- * {@link IBiometricsFace} only supports a single in-flight challenge. In cases where two
- * callers both need challenges (e.g. resetLockout right before enrollment), we need to ensure
- * that either:
- * 1) generateChallenge/operation/revokeChallenge is complete before the next generateChallenge
- * is processed by the scheduler, or
- * 2) the generateChallenge callback provides a mechanism for notifying the caller that its
- * challenge has been invalidated by a subsequent caller, as well as a mechanism for
- * notifying the previous caller that the interrupting operation is complete (e.g. the
- * interrupting client's challenge has been revoked, so that the interrupted client can
- * start retry logic if necessary). See
- * {@link
- *android.hardware.face.FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)}
- * The only case of conflicting challenges is currently resetLockout --> enroll. So, the second
- * option seems better as it prioritizes the new operation, which is user-facing.
+ * {@link IBiometricsFace} only supports a single in-flight challenge but there are cases where
+ * two callers both need challenges (e.g. resetLockout right before enrollment).
*/
@Override
public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
- if (mCurrentChallengeOwner != null) {
- final ClientMonitorCallbackConverter listener =
- mCurrentChallengeOwner.getListener();
- Slog.w(TAG, "Current challenge owner: " + mCurrentChallengeOwner
- + ", listener: " + listener
- + ", interrupted by: " + opPackageName);
- if (listener != null) {
- try {
- listener.onChallengeInterrupted(mSensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify challenge interrupted", e);
- }
- }
+ incrementChallengeCount();
+
+ if (isGeneratedChallengeCacheValid()) {
+ Slog.d(TAG, "Current challenge is cached and will be reused");
+ mGeneratedChallengeCache.reuseResult(receiver);
+ return;
}
scheduleUpdateActiveUserWithoutHandler(userId);
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId, mCurrentChallengeOwner);
+ opPackageName, mSensorId, sSystemClock.millis());
+ mGeneratedChallengeCache = client;
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
if (client != clientMonitor) {
Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client."
+ " Expecting: " + client + ", received: " + clientMonitor);
- return;
}
- Slog.d(TAG, "Current challenge owner: " + client);
- mCurrentChallengeOwner = client;
}
});
});
@@ -539,14 +541,16 @@
public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
- if (mCurrentChallengeOwner != null
- && !mCurrentChallengeOwner.getOwnerString().contentEquals(opPackageName)) {
- Slog.e(TAG, "scheduleRevokeChallenge, package: " + opPackageName
- + " attempting to revoke challenge owned by: "
- + mCurrentChallengeOwner.getOwnerString());
+ final boolean shouldRevoke = decrementChallengeCount() == 0;
+ if (!shouldRevoke) {
+ Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: "
+ + mGeneratedChallengeCount);
return;
}
+ Slog.d(TAG, "scheduleRevokeChallenge executing - no active clients");
+ mGeneratedChallengeCache = null;
+
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
mLazyDaemon, token, userId, opPackageName, mSensorId);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@@ -556,33 +560,6 @@
if (client != clientMonitor) {
Slog.e(TAG, "scheduleRevokeChallenge, mismatched client."
+ "Expecting: " + client + ", received: " + clientMonitor);
- return;
- }
-
- if (mCurrentChallengeOwner == null) {
- // Can happen if revoke is incorrectly called, for example without a
- // preceding generateChallenge
- Slog.w(TAG, "Current challenge owner is null");
- return;
- }
-
- final FaceGenerateChallengeClient previousChallengeOwner =
- mCurrentChallengeOwner.getInterruptedClient();
- mCurrentChallengeOwner = null;
-
- Slog.d(TAG, "Previous challenge owner: " + previousChallengeOwner);
- if (previousChallengeOwner != null) {
- final ClientMonitorCallbackConverter listener =
- previousChallengeOwner.getListener();
- if (listener == null) {
- Slog.w(TAG, "Listener is null");
- } else {
- try {
- listener.onChallengeInterruptFinished(mSensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify interrupt finished", e);
- }
- }
}
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index 3e0064e4..f418104 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -17,16 +17,20 @@
package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.IFaceServiceReceiver;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.internal.util.Preconditions;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Face-specific generateChallenge client supporting the
* {@link android.hardware.biometrics.face.V1_0} HIDL interface.
@@ -34,40 +38,70 @@
public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiometricsFace> {
private static final String TAG = "FaceGenerateChallengeClient";
- private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
+ static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
+ private static final Callback EMPTY_CALLBACK = new Callback() {
+ };
- // If `this` FaceGenerateChallengeClient was invoked while an existing in-flight challenge
- // was not revoked yet, store a reference to the interrupted client here. Notify the interrupted
- // client when `this` challenge is revoked.
- @Nullable private final FaceGenerateChallengeClient mInterruptedClient;
+ private final long mCreatedAt;
+ private List<IFaceServiceReceiver> mWaiting;
+ private Long mChallengeResult;
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId, @Nullable FaceGenerateChallengeClient interruptedClient) {
+ int sensorId, long now) {
super(context, lazyDaemon, token, listener, userId, owner, sensorId);
- mInterruptedClient = interruptedClient;
- }
-
- @Nullable
- public FaceGenerateChallengeClient getInterruptedClient() {
- return mInterruptedClient;
+ mCreatedAt = now;
+ mWaiting = new ArrayList<>();
}
@Override
protected void startHalOperation() {
+ mChallengeResult = null;
try {
- final long challenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
- try {
- getListener().onChallengeGenerated(getSensorId(), challenge);
- mCallback.onClientFinished(this, true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- mCallback.onClientFinished(this, false /* success */);
+ mChallengeResult = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+ // send the result to the original caller via mCallback and any waiting callers
+ // that called reuseResult
+ sendChallengeResult(getListener(), mCallback);
+ for (IFaceServiceReceiver receiver : mWaiting) {
+ sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK);
}
} catch (RemoteException e) {
Slog.e(TAG, "generateChallenge failed", e);
mCallback.onClientFinished(this, false /* success */);
+ } finally {
+ mWaiting = null;
+ }
+ }
+
+ /** @return An arbitrary time value for caching provided to the constructor. */
+ public long getCreatedAt() {
+ return mCreatedAt;
+ }
+
+ /**
+ * Reuse the result of this operation when it is available. The receiver will be notified
+ * immediately if a challenge has already been generated.
+ *
+ * @param receiver receiver to be notified of challenge result
+ */
+ public void reuseResult(@NonNull IFaceServiceReceiver receiver) {
+ if (mWaiting != null) {
+ mWaiting.add(receiver);
+ } else {
+ sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK);
+ }
+ }
+
+ private void sendChallengeResult(@NonNull ClientMonitorCallbackConverter receiver,
+ @NonNull Callback ownerCallback) {
+ Preconditions.checkState(mChallengeResult != null, "result not available");
+ try {
+ receiver.onChallengeGenerated(getSensorId(), getTargetUserId(), mChallengeResult);
+ ownerCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ ownerCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index e34afc0..29f2f20 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -100,7 +100,7 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 293b57d..6d01481 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -53,7 +53,7 @@
void onChallengeGenerated(int sensorId, int userId, long challenge) {
try {
- getListener().onChallengeGenerated(sensorId, challenge);
+ getListener().onChallengeGenerated(sensorId, userId, challenge);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send challenge", e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index ad4f679..c00daff 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -101,7 +101,7 @@
}
@Override
- public void onChallengeGenerated(int sensorId, long challenge) {
+ public void onChallengeGenerated(int sensorId, int userId, long challenge) {
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
index 3584397..db2f045 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
@@ -48,7 +48,7 @@
try {
final long challenge = getFreshDaemon().preEnroll();
try {
- getListener().onChallengeGenerated(getSensorId(), challenge);
+ getListener().onChallengeGenerated(getSensorId(), getTargetUserId(), challenge);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 28f208b..5886b1a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -338,7 +338,8 @@
return currentPermission;
}
try {
- final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
+ final PackageInfo app = mPackageManager.getPackageInfo(name,
+ GET_PERMISSIONS | MATCH_ANY_USER);
final boolean isNetwork = hasNetworkPermission(app);
final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
if (isNetwork || hasRestrictedPermission) {
@@ -664,6 +665,7 @@
break;
case INetd.PERMISSION_UNINSTALLED:
uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
default:
Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
+ netdPermissionsAppIds.keyAt(i));
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 10f6948f..dcd0eb8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -136,18 +136,19 @@
if (!mService.isControlEnabled()) {
return;
}
- if (isActiveSource()) {
- mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
- mAddress, mService.getPhysicalAddress()));
- }
boolean wasActiveSource = isActiveSource();
- // Invalidate the internal active source record when goes to standby
+ // Invalidate the internal active source record when going to standby
mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
"HdmiCecLocalDevicePlayback#onStandby()");
boolean mTvSendStandbyOnSleep = mService.getHdmiCecConfig().getIntValue(
HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
== HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
- if (initiatedByCec || !mTvSendStandbyOnSleep || !wasActiveSource) {
+ if (!wasActiveSource) {
+ return;
+ }
+ if (initiatedByCec || !mTvSendStandbyOnSleep) {
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(mAddress,
+ mService.getPhysicalAddress()));
return;
}
switch (standbyAction) {
@@ -167,6 +168,9 @@
Constants.ADDR_BROADCAST));
break;
case HdmiControlManager.POWER_CONTROL_MODE_NONE:
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildInactiveSource(mAddress,
+ mService.getPhysicalAddress()));
break;
}
break;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
index 7226cc2..e52e32a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
@@ -90,6 +90,8 @@
pw.println(" Set the value of a CEC setting");
pw.println(" setsystemaudiomode, setsam [on|off]");
pw.println(" Sets the System Audio Mode feature on or off on TV devices");
+ pw.println(" setarc [on|off]");
+ pw.println(" Sets the ARC feature on or off on TV devices");
}
private int handleShellCommand(String cmd) throws RemoteException {
@@ -106,6 +108,8 @@
case "setsystemaudiomode":
case "setsam":
return setSystemAudioMode(pw);
+ case "setarc":
+ return setArcMode(pw);
}
getErrPrintWriter().println("Unhandled command: " + cmd);
@@ -229,6 +233,27 @@
return mCecResult.get() == HdmiControlManager.RESULT_SUCCESS ? 0 : 1;
}
+ private int setArcMode(PrintWriter pw) throws RemoteException {
+ if (1 > getRemainingArgsCount()) {
+ throw new IllegalArgumentException(
+ "Please indicate if ARC mode should be turned \"on\" or \"off\".");
+ }
+
+ String arg = getNextArg();
+ if (arg.equals("on")) {
+ pw.println("Setting ARC mode on");
+ mBinderService.setArcMode(true);
+ } else if (arg.equals("off")) {
+ pw.println("Setting ARC mode off");
+ mBinderService.setArcMode(false);
+ } else {
+ throw new IllegalArgumentException(
+ "Please indicate if ARC mode should be turned \"on\" or \"off\".");
+ }
+
+ return 0;
+ }
+
private boolean receiveCallback(String command) {
try {
if (!mLatch.await(HdmiConfig.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index 5abc438..2bdeab4 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -98,19 +98,7 @@
}
@Override
- public void onChallengeInterrupted(int sensorId) {
- Slog.w(TAG, "Challenge interrupted, sensor: " + sensorId);
- // Consider re-attempting generateChallenge/resetLockout/revokeChallenge
- // when onChallengeInterruptFinished is invoked
- }
-
- @Override
- public void onChallengeInterruptFinished(int sensorId) {
- Slog.w(TAG, "Challenge interrupt finished, sensor: " + sensorId);
- }
-
- @Override
- public void onGenerateChallengeResult(int sensorId, long challenge) {
+ public void onGenerateChallengeResult(int sensorId, int userId, long challenge) {
if (!sensorIds.contains(sensorId)) {
Slog.e(TAG, "Unknown sensorId received: " + sensorId);
return;
@@ -128,10 +116,6 @@
}
sensorIds.remove(sensorId);
- // Challenge is only required for IBiometricsFace@1.0 (and not IFace AIDL). The
- // IBiometricsFace@1.0 HAL does not require userId to revokeChallenge, so passing
- // in 0 is OK.
- final int userId = 0;
faceManager.revokeChallenge(sensorId, userId, challenge);
if (sensorIds.isEmpty()) {
@@ -234,18 +218,12 @@
}
}
- /**
- * For devices on {@link android.hardware.biometrics.face.V1_0} which only support a single
- * in-flight challenge, we generate a single challenge to reset lockout for all profiles. This
- * hopefully reduces/eliminates issues such as overwritten challenge, incorrectly revoked
- * challenge, or other race conditions.
- */
private void processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts) {
if (mFaceManager != null) {
if (mFaceResetLockoutTask != null) {
// This code will need to be updated if this problem ever occurs.
- Slog.w(TAG, "mFaceGenerateChallengeCallback not null, previous operation may be"
- + " stuck");
+ Slog.w(TAG,
+ "mFaceGenerateChallengeCallback not null, previous operation may be stuck");
}
final List<FaceSensorPropertiesInternal> faceSensorProperties =
mFaceManager.getSensorPropertiesInternal();
@@ -258,12 +236,13 @@
mSpManager, sensorIds, pendingResetLockouts);
for (final FaceSensorPropertiesInternal prop : faceSensorProperties) {
if (prop.resetLockoutRequiresHardwareAuthToken) {
- if (prop.resetLockoutRequiresChallenge) {
- // Generate a challenge for each sensor. The challenge does not need to be
- // per-user, since the HAT returned by gatekeeper contains userId.
- mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask);
- } else {
- for (UserAuthInfo user : pendingResetLockouts) {
+ for (UserAuthInfo user : pendingResetLockouts) {
+ if (prop.resetLockoutRequiresChallenge) {
+ Slog.d(TAG, "Generating challenge for sensor: " + prop.sensorId
+ + ", user: " + user.userId);
+ mFaceManager.generateChallenge(prop.sensorId, user.userId,
+ mFaceResetLockoutTask);
+ } else {
Slog.d(TAG, "Resetting face lockout for sensor: " + prop.sensorId
+ ", user: " + user.userId);
final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 805f395..8c1fd36 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1511,7 +1511,8 @@
builder.setSmallIcon(R.drawable.stat_notify_error);
- final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
+ final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template,
+ mContext.getPackageName());
builder.setDeleteIntent(PendingIntent.getBroadcast(
mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
@@ -1597,7 +1598,8 @@
builder.setSmallIcon(R.drawable.stat_notify_error);
- final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template);
+ final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template,
+ mContext.getPackageName());
builder.setDeleteIntent(PendingIntent.getBroadcast(
mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
@@ -5478,17 +5480,19 @@
return new Intent(ACTION_ALLOW_BACKGROUND);
}
- private static Intent buildSnoozeWarningIntent(NetworkTemplate template) {
+ private static Intent buildSnoozeWarningIntent(NetworkTemplate template, String targetPackage) {
final Intent intent = new Intent(ACTION_SNOOZE_WARNING);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+ intent.setPackage(targetPackage);
return intent;
}
- private static Intent buildSnoozeRapidIntent(NetworkTemplate template) {
+ private static Intent buildSnoozeRapidIntent(NetworkTemplate template, String targetPackage) {
final Intent intent = new Intent(ACTION_SNOOZE_RAPID);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+ intent.setPackage(targetPackage);
return intent;
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 202b315..a3daae4 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -101,6 +101,7 @@
protected static final String ENABLED_SERVICES_SEPARATOR = ":";
private static final String DB_VERSION_1 = "1";
private static final String DB_VERSION_2 = "2";
+ private static final String DB_VERSION_3 = "3";
/**
@@ -113,8 +114,9 @@
static final String ATT_VERSION = "version";
static final String ATT_DEFAULTS = "defaults";
static final String ATT_USER_SET = "user_set_services";
+ static final String ATT_USER_CHANGED = "user_changed";
- static final int DB_VERSION = 3;
+ static final int DB_VERSION = 4;
static final int APPROVAL_BY_PACKAGE = 0;
static final int APPROVAL_BY_COMPONENT = 1;
@@ -160,6 +162,8 @@
@GuardedBy("mApproved")
protected ArrayMap<Integer, ArraySet<String>> mUserSetServices = new ArrayMap<>();
+ protected ArrayMap<Integer, Boolean> mIsUserChanged = new ArrayMap<>();
+
// True if approved services are stored in xml, not settings.
private boolean mUseXml;
@@ -338,6 +342,7 @@
for (int i = 0; i < N; i++) {
final int userId = mApproved.keyAt(i);
final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+ final Boolean userChanged = mIsUserChanged.get(userId);
if (approvedByType != null) {
final int M = approvedByType.size();
for (int j = 0; j < M; j++) {
@@ -345,16 +350,20 @@
final ArraySet<String> approved = approvedByType.valueAt(j);
if (approvedByType != null && approvedByType.size() > 0) {
pw.println(" " + String.join(ENABLED_SERVICES_SEPARATOR, approved)
- + " (user: " + userId + " isPrimary: " + isPrimary + ")");
+ + " (user: " + userId + " isPrimary: " + isPrimary
+ + (userChanged == null ? "" : " isUserChanged: "
+ + userChanged) + ")");
}
}
}
}
-
pw.println(" Has user set:");
Set<Integer> userIds = mUserSetServices.keySet();
for (int userId : userIds) {
- pw.println(" userId=" + userId + " value=" + mUserSetServices.get(userId));
+ if (mIsUserChanged.get(userId) == null) {
+ pw.println(" userId=" + userId + " value="
+ + (mUserSetServices.get(userId)));
+ }
}
}
@@ -489,13 +498,14 @@
continue;
}
final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+ final Boolean isUserChanged = mIsUserChanged.get(approvedUserId);
if (approvedByType != null) {
final int M = approvedByType.size();
for (int j = 0; j < M; j++) {
final boolean isPrimary = approvedByType.keyAt(j);
final Set<String> approved = approvedByType.valueAt(j);
final Set<String> userSet = mUserSetServices.get(approvedUserId);
- if (approved != null || userSet != null) {
+ if (approved != null || userSet != null || isUserChanged != null) {
String allowedItems = approved == null
? ""
: String.join(ENABLED_SERVICES_SEPARATOR, approved);
@@ -503,7 +513,9 @@
out.attribute(null, ATT_APPROVED_LIST, allowedItems);
out.attributeInt(null, ATT_USER_ID, approvedUserId);
out.attributeBoolean(null, ATT_IS_PRIMARY, isPrimary);
- if (userSet != null) {
+ if (isUserChanged != null) {
+ out.attributeBoolean(null, ATT_USER_CHANGED, isUserChanged);
+ } else if (userSet != null) {
String userSetItems =
String.join(ENABLED_SERVICES_SEPARATOR, userSet);
out.attribute(null, ATT_USER_SET, userSetItems);
@@ -618,12 +630,21 @@
? userId : parser.getAttributeInt(null, ATT_USER_ID, 0);
final boolean isPrimary =
parser.getAttributeBoolean(null, ATT_IS_PRIMARY, true);
- final String userSet = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
+
+ final String isUserChanged = XmlUtils.readStringAttribute(parser,
+ ATT_USER_CHANGED);
+ String userSetComponent = null;
+ if (isUserChanged == null) {
+ userSetComponent = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
+ } else {
+ mIsUserChanged.put(resolvedUserId, Boolean.valueOf(isUserChanged));
+ }
readExtraAttributes(tag, parser, resolvedUserId);
if (allowedManagedServicePackages == null || allowedManagedServicePackages.test(
- getPackageName(approved), resolvedUserId, getRequiredPermission())) {
+ getPackageName(approved), resolvedUserId, getRequiredPermission())
+ || approved.isEmpty()) {
if (mUm.getUserInfo(resolvedUserId) != null) {
- addApprovedList(approved, resolvedUserId, isPrimary, userSet);
+ addApprovedList(approved, resolvedUserId, isPrimary, userSetComponent);
}
mUseXml = true;
}
@@ -634,10 +655,16 @@
}
boolean isOldVersion = TextUtils.isEmpty(version)
|| DB_VERSION_1.equals(version)
- || DB_VERSION_2.equals(version);
+ || DB_VERSION_2.equals(version)
+ || DB_VERSION_3.equals(version);
+ boolean needUpgradeUserset = DB_VERSION_3.equals(version);
if (isOldVersion) {
upgradeDefaultsXmlVersion();
}
+ if (needUpgradeUserset) {
+ upgradeUserSet();
+ }
+
rebindServices(false, USER_ALL);
}
@@ -666,6 +693,8 @@
}
}
+ protected void upgradeUserSet() {};
+
/**
* Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag.
*/
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 67fd09b3..19c717d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -106,7 +106,6 @@
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.internal.util.CollectionUtils.emptyIfNull;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -324,7 +323,6 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
@@ -832,7 +830,7 @@
learnNASPendingIntent).build();
- return new Notification.Builder(getContext(), SystemNotificationChannels.ALERTS)
+ return new Notification.Builder(getContext(), SystemNotificationChannels.SYSTEM_CHANGES)
.setAutoCancel(false)
.setOngoing(true)
.setTicker(getContext().getResources().getString(title))
@@ -9467,6 +9465,27 @@
return mDefaultFromConfig;
}
+ @Override
+ protected void upgradeUserSet() {
+ for (int userId: mApproved.keySet()) {
+ ArraySet<String> userSetServices = mUserSetServices.get(userId);
+ mIsUserChanged.put(userId, (userSetServices != null && userSetServices.size() > 0));
+ }
+ }
+
+ @Override
+ protected void addApprovedList(String approved, int userId, boolean isPrimary,
+ String userSet) {
+ if (!TextUtils.isEmpty(approved)) {
+ String[] approvedArray = approved.split(ENABLED_SERVICES_SEPARATOR);
+ if (approvedArray.length > 1) {
+ Slog.d(TAG, "More than one approved assistants");
+ approved = approvedArray[0];
+ }
+ }
+ super.addApprovedList(approved, userId, isPrimary, userSet);
+ }
+
public NotificationAssistants(Context context, Object lock, UserProfiles up,
IPackageManager pm) {
super(context, lock, up, pm);
@@ -9641,47 +9660,12 @@
}
boolean hasUserSet(int userId) {
- synchronized (mLock) {
- ArraySet<String> userSetServices = mUserSetServices.get(userId);
- if (userSetServices == null) {
- // Legacy case - no data means user-set, unless no assistant is set
- return !mApproved.isEmpty();
- }
- Map<Boolean, ArraySet<String>> approvedByType = emptyIfNull(mApproved.get(userId));
- return userSetServices.containsAll(emptyIfNull(approvedByType.get(true)))
- && userSetServices.containsAll(emptyIfNull(approvedByType.get(false)));
- }
+ Boolean userSet = mIsUserChanged.get(userId);
+ return (userSet != null && userSet);
}
void setUserSet(int userId, boolean set) {
- synchronized (mLock) {
- ArraySet<String> userSetServices = new ArraySet<>();
- if (set) {
- ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
- if (approvedByType != null) {
- for (int i = 0; i < approvedByType.size(); i++) {
- userSetServices.addAll(approvedByType.valueAt(i));
- }
- }
- }
- mUserSetServices.put(userId, userSetServices);
- }
- }
-
- @Override
- protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId)
- throws IOException {
- // TODO: this logic looks broken, since it's trying to convert a
- // list into a boolean; for now we preserve the old parsing behavior
- // to avoid a performance regression, but someone should investigate
- final String value = parser.getAttributeValue(null, ATT_USER_SET);
- final boolean userSet;
- if (TextUtils.isEmpty(value)) {
- userSet = false;
- } else {
- userSet = Boolean.parseBoolean(value);
- }
- setUserSet(userId, userSet);
+ mIsUserChanged.put(userId, set);
}
private void notifyCapabilitiesChanged(final ManagedServiceInfo info) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 7f18c4b..20f35f2 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2254,24 +2254,6 @@
}
@Override
- public AndroidFuture updateShortcutVisibility(String callingPkg, String packageName,
- byte[] certificate, boolean visible, int userId) {
- final AndroidFuture<Void> ret = new AndroidFuture<>();
- injectPostToHandlerIfAppSearch(() -> {
- try {
- synchronized (mLock) {
- getPackageShortcutsForPublisherLocked(callingPkg, userId)
- .updateVisibility(packageName, certificate, visible);
- }
- ret.complete(null);
- } catch (Exception e) {
- ret.completeExceptionally(e);
- }
- });
- return ret;
- }
-
- @Override
public AndroidFuture requestPinShortcut(String packageName, ShortcutInfo shortcut,
IntentSender resultIntent, int userId) {
final AndroidFuture<Boolean> ret = new AndroidFuture<>();
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 90a3c58..f86eb60 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -94,6 +94,9 @@
"include-filter": "com.android.server.pm.test.OverlayActorVisibilityTest"
}
]
+ },
+ {
+ "name": "CtsPackageManagerBootTestCases"
}
],
"postsubmit": [
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 206f67e..8800a9e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1562,6 +1562,10 @@
// If the transition has not started yet, the activity must be the top.
return false;
}
+ if (mLastWallpaperVisible && r.windowsCanBeWallpaperTarget()) {
+ // Use normal rotation animation for orientation change of visible wallpaper.
+ return false;
+ }
final int rotation = rotationForActivityInDifferentOrientation(r);
if (rotation == ROTATION_UNDEFINED) {
// The display rotation won't be changed by current top activity. The client side
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fb481b4..a4c8db6 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -38,6 +38,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
@@ -75,6 +76,9 @@
private static final int REPORT_CONFIGS = CONTROLLABLE_CONFIGS;
private static final int REPORT_WINDOW_CONFIGS = CONTROLLABLE_WINDOW_CONFIGS;
+ private static final boolean DEBUG_ENABLE_REVEAL_ANIMATION =
+ SystemProperties.getBoolean("persist.debug.enable_reveal_animation", false);
+
// The set of modes that are currently supports
// TODO: Remove once the task organizer can support all modes
@VisibleForTesting
@@ -183,7 +187,7 @@
SurfaceControl windowAnimationLeash = null;
Rect mainFrame = null;
final boolean playShiftUpAnimation = !task.inMultiWindowMode();
- if (prepareAnimation && playShiftUpAnimation) {
+ if (prepareAnimation && playShiftUpAnimation && DEBUG_ENABLE_REVEAL_ANIMATION) {
final ActivityRecord topActivity = task.topActivityWithStartingWindow();
if (topActivity != null) {
final WindowState mainWindow =
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 51b270c..24699d9 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -983,7 +983,8 @@
bool enableReadTimeouts = ifs.readTimeoutsRequested();
std::lock_guard l(mMountOperationLock);
- auto status = mVold->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
+ auto status = mVold->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts,
+ ifs.metricsKey);
if (status.isOk()) {
// Store states.
ifs.setReadLogsEnabled(enableReadLogs);
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 68a28b2..ce3d514 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -56,8 +56,10 @@
}
binder::Status setIncFsMountOptions(
const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
- bool enableReadLogs, bool enableReadTimeouts) const final {
- return mInterface->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
+ bool enableReadLogs, bool enableReadTimeouts,
+ const std::string& sysfsName) const final {
+ return mInterface->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts,
+ sysfsName);
}
private:
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index c0ef7ba..39e2ee3 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -58,7 +58,7 @@
const std::string& targetDir) const = 0;
virtual binder::Status setIncFsMountOptions(
const os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs,
- bool enableReadTimeouts) const = 0;
+ bool enableReadTimeouts, const std::string& sysfsName) const = 0;
};
class DataLoaderManagerWrapper {
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 6c9310b..fae654e 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -56,10 +56,10 @@
MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir));
MOCK_CONST_METHOD2(bindMount,
binder::Status(const std::string& sourceDir, const std::string& argetDir));
- MOCK_CONST_METHOD3(
+ MOCK_CONST_METHOD4(
setIncFsMountOptions,
binder::Status(const ::android::os::incremental::IncrementalFileSystemControlParcel&,
- bool, bool));
+ bool, bool, const std::string&));
void mountIncFsFails() {
ON_CALL(*this, mountIncFs(_, _, _, _, _))
@@ -83,12 +83,12 @@
ON_CALL(*this, bindMount(_, _)).WillByDefault(Return(binder::Status::ok()));
}
void setIncFsMountOptionsFails() const {
- ON_CALL(*this, setIncFsMountOptions(_, _, _))
+ ON_CALL(*this, setIncFsMountOptions(_, _, _, _))
.WillByDefault(Return(
binder::Status::fromExceptionCode(1, String8("failed to set options"))));
}
void setIncFsMountOptionsSuccess() {
- ON_CALL(*this, setIncFsMountOptions(_, _, _))
+ ON_CALL(*this, setIncFsMountOptions(_, _, _, _))
.WillByDefault(Invoke(this, &MockVoldService::setIncFsMountOptionsOk));
}
binder::Status getInvalidControlParcel(const std::string& imagePath,
@@ -108,7 +108,7 @@
}
binder::Status setIncFsMountOptionsOk(
const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
- bool enableReadLogs, bool enableReadTimeouts) {
+ bool enableReadLogs, bool enableReadTimeouts, const std::string& sysfsName) {
mReadLogsEnabled = enableReadLogs;
mReadTimeoutsEnabled = enableReadTimeouts;
return binder::Status::ok();
@@ -1451,9 +1451,9 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// on startLoading
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
// We are calling setIncFsMountOptions(true).
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1475,8 +1475,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(2);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1503,8 +1503,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(2);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(2);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1544,8 +1544,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(3);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(3);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1585,8 +1585,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(5);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(3);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(5);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(3);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1660,9 +1660,9 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// We are calling setIncFsMountOptions(true).
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
// setIncFsMountOptions(false) is called on the callback.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(2);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// After callback is called, disable read logs and remove callback.
@@ -1685,8 +1685,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// checkPermission fails, no calls to set opitions, start or stop WatchingMode.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(0);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
@@ -1705,8 +1705,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// checkPermission fails, no calls to set opitions, start or stop WatchingMode.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(0);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
@@ -1726,8 +1726,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// We are calling setIncFsMountOptions.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
// setIncFsMountOptions fails, no calls to start or stop WatchingMode.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
diff --git a/services/net/TEST_MAPPING b/services/net/TEST_MAPPING
index 7025dd1..7eca1f9 100644
--- a/services/net/TEST_MAPPING
+++ b/services/net/TEST_MAPPING
@@ -2,6 +2,9 @@
"imports": [
{
"path": "frameworks/base/core/java/android/net"
+ },
+ {
+ "path": "packages/modules/Wifi/framework"
}
]
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
index 8fe1139..d3feb12 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
@@ -75,10 +75,14 @@
.setPropertyName(propertyKeyString)
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setExactMatchPosition(29)
- .setExactMatchBytes(3)
- .setWindowPosition(26)
- .setWindowBytes(6)))
+ .setExactMatchBytePosition(29)
+ .setExactMatchByteLength(3)
+ .setExactMatchUtf16Position(29)
+ .setExactMatchUtf16Length(3)
+ .setWindowBytePosition(26)
+ .setWindowByteLength(6)
+ .setWindowUtf16Position(26)
+ .setWindowUtf16Length(6)))
.build();
SearchResultProto searchResultProto =
SearchResultProto.newBuilder()
@@ -168,19 +172,27 @@
.setPropertyName("senderName")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setExactMatchPosition(0)
- .setExactMatchBytes(4)
- .setWindowPosition(0)
- .setWindowBytes(9)))
+ .setExactMatchBytePosition(0)
+ .setExactMatchByteLength(4)
+ .setExactMatchUtf16Position(0)
+ .setExactMatchUtf16Length(4)
+ .setWindowBytePosition(0)
+ .setWindowByteLength(9)
+ .setWindowUtf16Position(0)
+ .setWindowUtf16Length(9)))
.addEntries(
SnippetProto.EntryProto.newBuilder()
.setPropertyName("senderEmail")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setExactMatchPosition(0)
- .setExactMatchBytes(20)
- .setWindowPosition(0)
- .setWindowBytes(20)))
+ .setExactMatchBytePosition(0)
+ .setExactMatchByteLength(20)
+ .setExactMatchUtf16Position(0)
+ .setExactMatchUtf16Length(20)
+ .setWindowBytePosition(0)
+ .setWindowByteLength(20)
+ .setWindowUtf16Position(0)
+ .setWindowUtf16Length(20)))
.build();
SearchResultProto searchResultProto =
SearchResultProto.newBuilder()
@@ -251,19 +263,27 @@
.setPropertyName("sender.name")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setExactMatchPosition(0)
- .setExactMatchBytes(4)
- .setWindowPosition(0)
- .setWindowBytes(9)))
+ .setExactMatchBytePosition(0)
+ .setExactMatchByteLength(4)
+ .setExactMatchUtf16Position(0)
+ .setExactMatchUtf16Length(4)
+ .setWindowBytePosition(0)
+ .setWindowByteLength(9)
+ .setWindowUtf16Position(0)
+ .setWindowUtf16Length(9)))
.addEntries(
SnippetProto.EntryProto.newBuilder()
.setPropertyName("sender.email[1]")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setExactMatchPosition(0)
- .setExactMatchBytes(21)
- .setWindowPosition(0)
- .setWindowBytes(21)))
+ .setExactMatchBytePosition(0)
+ .setExactMatchByteLength(21)
+ .setExactMatchUtf16Position(0)
+ .setExactMatchUtf16Length(21)
+ .setWindowBytePosition(0)
+ .setWindowByteLength(21)
+ .setWindowUtf16Position(0)
+ .setWindowUtf16Length(21)))
.build();
SearchResultProto searchResultProto =
SearchResultProto.newBuilder()
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
index 5de8a7a..217430c 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
@@ -89,7 +89,7 @@
CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo(
TEST_DEFAULT_SAMPLING_RATIO);
assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+ CallStats.CALL_TYPE_SEARCH).mSamplingRatio).isEqualTo(
TEST_DEFAULT_SAMPLING_RATIO);
assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo(
@@ -103,7 +103,7 @@
int querySamplingRatio = 2;
final SparseIntArray samplingRatios = new SparseIntArray();
samplingRatios.put(CallStats.CALL_TYPE_PUT_DOCUMENT, putDocumentSamplingRatio);
- samplingRatios.put(CallStats.CALL_TYPE_QUERY, querySamplingRatio);
+ samplingRatios.put(CallStats.CALL_TYPE_SEARCH, querySamplingRatio);
PlatformLogger logger = new PlatformLogger(
ApplicationProvider.getApplicationContext(),
UserHandle.USER_NULL,
@@ -127,7 +127,7 @@
CallStats.CALL_TYPE_PUT_DOCUMENT).mSamplingRatio).isEqualTo(
putDocumentSamplingRatio);
assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
- CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+ CallStats.CALL_TYPE_SEARCH).mSamplingRatio).isEqualTo(
querySamplingRatio);
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 0b59be6..39c51d5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -18,6 +18,10 @@
import static junit.framework.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -26,6 +30,7 @@
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
import android.os.IBinder;
import android.os.UserManager;
@@ -42,8 +47,12 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.IntStream;
@Presubmit
@SmallTest
@@ -51,6 +60,7 @@
private static final String TAG = "Face10Test";
private static final int SENSOR_ID = 1;
+ private static final int USER_ID = 20;
@Mock
private Context mContext;
@@ -86,10 +96,18 @@
FaceSensorProperties.TYPE_UNKNOWN, supportsFaceDetection, supportsSelfIllumination,
resetLockoutRequiresChallenge);
+ Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST"));
mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mScheduler);
mBinder = new Binder();
}
+ private void tick(long seconds) {
+ waitForIdle();
+ Face10.sSystemClock = Clock.fixed(Instant.ofEpochSecond(
+ Face10.sSystemClock.instant().getEpochSecond() + seconds),
+ ZoneId.of("PST"));
+ }
+
@Test
public void getAuthenticatorId_doesNotCrashWhenIdNotFound() {
assertEquals(0, mFace10.getAuthenticatorId(0 /* sensorId */, 111 /* userId */));
@@ -104,6 +122,59 @@
}
@Test
+ public void scheduleGenerateChallenge_cachesResult() {
+ final IFaceServiceReceiver[] mocks = IntStream.range(0, 3)
+ .mapToObj(i -> mock(IFaceServiceReceiver.class))
+ .toArray(IFaceServiceReceiver[]::new);
+ for (IFaceServiceReceiver mock : mocks) {
+ mFace10.scheduleGenerateChallenge(SENSOR_ID, USER_ID, mBinder, mock, TAG);
+ tick(10);
+ }
+ tick(120);
+ mFace10.scheduleGenerateChallenge(
+ SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+ waitForIdle();
+
+ verify(mScheduler, times(2))
+ .scheduleClientMonitor(isA(FaceGenerateChallengeClient.class), any());
+ }
+
+ @Test
+ public void scheduleRevokeChallenge_waitsUntilEmpty() {
+ final long challenge = 22;
+ final IFaceServiceReceiver[] mocks = IntStream.range(0, 3)
+ .mapToObj(i -> mock(IFaceServiceReceiver.class))
+ .toArray(IFaceServiceReceiver[]::new);
+ for (IFaceServiceReceiver mock : mocks) {
+ mFace10.scheduleGenerateChallenge(SENSOR_ID, USER_ID, mBinder, mock, TAG);
+ tick(10);
+ }
+ for (IFaceServiceReceiver mock : mocks) {
+ mFace10.scheduleRevokeChallenge(SENSOR_ID, USER_ID, mBinder, TAG, challenge);
+ tick(10);
+ }
+ waitForIdle();
+
+ verify(mScheduler).scheduleClientMonitor(isA(FaceRevokeChallengeClient.class), any());
+ }
+
+ @Test
+ public void scheduleRevokeChallenge_doesNotWaitForever() {
+ mFace10.scheduleGenerateChallenge(
+ SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+ mFace10.scheduleGenerateChallenge(
+ SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+ tick(10000);
+ mFace10.scheduleGenerateChallenge(
+ SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+ mFace10.scheduleRevokeChallenge(
+ SENSOR_ID, USER_ID, mBinder, TAG, 8 /* challenge */);
+ waitForIdle();
+
+ verify(mScheduler).scheduleClientMonitor(isA(FaceRevokeChallengeClient.class), any());
+ }
+
+ @Test
public void halServiceDied_resetsScheduler() {
// It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
// serviceDied directly.
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
new file mode 100644
index 0000000..55dc035
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.hidl;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalUint64;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@SmallTest
+public class FaceGenerateChallengeClientTest {
+
+ private static final String TAG = "FaceGenerateChallengeClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final long START_TIME = 5000;
+ private static final long CHALLENGE = 200;
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Mock
+ private IBiometricsFace mIBiometricsFace;
+ @Mock
+ private IFaceServiceReceiver mClientReceiver;
+ @Mock
+ private IFaceServiceReceiver mOtherReceiver;
+ @Mock
+ private BaseClientMonitor.Callback mMonitorCallback;
+
+ private FaceGenerateChallengeClient mClient;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ final OptionalUint64 challenge = new OptionalUint64();
+ challenge.value = CHALLENGE;
+ when(mIBiometricsFace.generateChallenge(anyInt())).thenReturn(challenge);
+
+ mClient = new FaceGenerateChallengeClient(mContext, () -> mIBiometricsFace, new Binder(),
+ new ClientMonitorCallbackConverter(mClientReceiver), USER_ID,
+ TAG, SENSOR_ID, START_TIME);
+ }
+
+ @Test
+ public void getCreatedAt() {
+ assertEquals(START_TIME, mClient.getCreatedAt());
+ }
+
+ @Test
+ public void reuseResult_whenNotReady() throws Exception {
+ mClient.reuseResult(mOtherReceiver);
+ verify(mOtherReceiver, never()).onChallengeGenerated(anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void reuseResult_whenReady() throws Exception {
+ mClient.start(mMonitorCallback);
+ mClient.reuseResult(mOtherReceiver);
+ verify(mOtherReceiver).onChallengeGenerated(eq(SENSOR_ID), eq(USER_ID), eq(CHALLENGE));
+ }
+
+ @Test
+ public void reuseResult_whenReallyReady() throws Exception {
+ mClient.reuseResult(mOtherReceiver);
+ mClient.start(mMonitorCallback);
+ verify(mOtherReceiver).onChallengeGenerated(eq(SENSOR_ID), eq(USER_ID), eq(CHALLENGE));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index c08857c..6cc8d471 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -68,10 +68,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -97,7 +99,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index ee9de07..97bd066 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -70,10 +70,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiControlService hdmiControlService =
@@ -89,7 +91,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index d5df071..29c9b40 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -71,10 +71,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiControlService hdmiControlService =
@@ -85,7 +87,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 4f97c26..650ffe9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -80,10 +80,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -109,7 +111,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
index 678f8b2..fa5cb67 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
@@ -103,8 +103,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -129,7 +127,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index 45409c8..29f62b5 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
@@ -96,9 +97,10 @@
mContextSpy = spy(new ContextWrapper(
InstrumentationRegistry.getInstrumentation().getTargetContext()));
- PowerManager powerManager = new PowerManager(
- mContextSpy, mIPowerManagerMock, mIThermalServiceMock, new Handler(mLooper));
- doReturn(powerManager).when(mContextSpy).getSystemService(Context.POWER_SERVICE);
+
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mLooper)));
doReturn(true).when(mIPowerManagerMock).isInteractive();
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 38a44c6..7911c40 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -94,8 +94,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -183,7 +181,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 1ac0150..5fbf8de 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -92,8 +92,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -140,7 +138,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
};
@@ -168,6 +167,8 @@
mPlaybackLogicalAddress = mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress();
mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
mNativeWrapper.clearResultMessages();
+ mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
+ HdmiProperties.playback_device_action_on_routing_control_values.NONE;
}
@Test
@@ -698,9 +699,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -720,9 +724,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -742,9 +749,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -764,9 +774,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -786,9 +799,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -808,9 +824,37 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
+ }
+
+ @Test
+ public void handleOnStandby_CecMessageReceived() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV);
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
+ mHdmiCecLocalDevicePlayback.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 8ee983f..59711a6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -86,8 +86,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -118,7 +116,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 1c7ff42..572ffd9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -75,10 +75,12 @@
Context contextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
Looper myLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(contextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper));
- when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(contextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(contextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
+ when(contextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(contextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(contextSpy) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index b1d77d0..bcf30a2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -192,10 +192,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, null);
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, null));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, null));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 826438f..4cd17e8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -91,10 +91,12 @@
setHdmiControlEnabled(hdmiControlEnabled);
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -120,7 +122,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 53b4b49..a9880c0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -78,10 +78,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy,
@@ -108,7 +110,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 865eb7a..2cf4ef1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -78,10 +78,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
Looper myLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -107,7 +109,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index 478aa41..9598a00 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -15,20 +15,11 @@
*/
package com.android.server.pm;
-import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
-
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
import android.app.PendingIntent;
-import android.app.appsearch.PackageIdentifier;
-import android.content.pm.AppSearchShortcutInfo;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
-
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-
-import java.util.Random;
/**
* Tests for {@link android.app.appsearch.AppSearchManager} and relevant APIs in ShortcutManager.
@@ -37,26 +28,6 @@
*/
public class ShortcutManagerTest12 extends BaseShortcutManagerTest {
- public void testUpdateShortcutVisibility_updatesShortcutSchema() {
- if (!DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.DARK_LAUNCH_REMOTE_PREDICTION_SERVICE_ENABLED,
- false)) {
- // no-op if app-search integration is disabled.
- return;
- }
- final byte[] cert = new byte[20];
- new Random().nextBytes(cert);
-
- runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- mManager.updateShortcutVisibility(CALLING_PACKAGE_2, cert, true);
- assertTrue(mMockAppSearchManager.mSchemasPackageAccessible.containsKey(
- AppSearchShortcutInfo.SCHEMA_TYPE));
- assertTrue(mMockAppSearchManager.mSchemasPackageAccessible.get(
- AppSearchShortcutInfo.SCHEMA_TYPE).get(0).equals(
- new PackageIdentifier(CALLING_PACKAGE_2, cert)));
- });
- }
-
public void testGetShortcutIntents_ReturnsMutablePendingIntents() throws RemoteException {
setDefaultLauncher(USER_0, LAUNCHER_1);
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS b/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS
new file mode 100644
index 0000000..08276f5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/power/batterysaver/OWNERS
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index c502ed5..825e53e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -100,6 +100,7 @@
private String mVersionString;
private final Set<ComponentName> mDefaults = new ArraySet();
private ManagedServices mService;
+ private String mUserSetString;
private static final String SETTING = "setting";
private static final String SECONDARY_SETTING = "secondary_setting";
@@ -138,7 +139,7 @@
profileIds.add(13);
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
- mVersionString = "2";
+ mVersionString = "4";
mExpectedPrimary = new ArrayMap<>();
mExpectedSecondary = new ArrayMap<>();
mExpectedPrimaryPackages = new ArrayMap<>();
@@ -1363,6 +1364,7 @@
public void loadDefaults_noVersionWithDefaults() throws Exception {
resetComponentsAndPackages();
mDefaults.add(new ComponentName("default", "class"));
+ mVersionString = null;
loadXml(mService);
assertEquals(mService.getDefaultComponents(), mDefaults);
}
@@ -1421,12 +1423,34 @@
resetComponentsAndPackages();
mDefaults.add(new ComponentName("default", "class"));
mDefaultsString = "xml/class";
- mVersionString = String.valueOf(mService.DB_VERSION);
loadXml(mService);
assertEquals(mService.getDefaultComponents(),
new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
}
+ @Test
+ public void upgradeUserSet_versionThree() throws Exception {
+ resetComponentsAndPackages();
+
+ List<UserInfo> users = new ArrayList<>();
+ users.add(new UserInfo(98, "98", 0));
+ users.add(new UserInfo(99, "99", 0));
+ for (UserInfo user : users) {
+ when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
+ }
+
+ mDefaultsString = "xml/class";
+ mVersionString = "3";
+ mUserSetString = "xml/class";
+ loadXml(mService);
+
+ //Test services without overriding upgradeUserSet() remain unchanged
+ assertEquals(new ArraySet(Arrays.asList(mUserSetString)),
+ mService.mUserSetServices.get(98));
+ assertEquals(new ArraySet(Arrays.asList(mUserSetString)),
+ mService.mUserSetServices.get(99));
+ assertEquals(new ArrayMap(), mService.mIsUserChanged);
+ }
private void resetComponentsAndPackages() {
ArrayMap<Integer, ArrayMap<Integer, String>> empty = new ArrayMap(1);
@@ -1468,11 +1492,17 @@
xml.append("<" + ManagedServices.TAG_MANAGED_SERVICES + " "
+ ManagedServices.ATT_USER_ID + "=\"99\" "
+ ManagedServices.ATT_IS_PRIMARY + "=\"true\" "
- + ManagedServices.ATT_APPROVED_LIST + "=\"990\" />\n");
+ + ManagedServices.ATT_APPROVED_LIST + "=\"990\" "
+ + (mUserSetString != null ? ManagedServices.ATT_USER_SET + "=\""
+ + mUserSetString + "\" " : "")
+ + "/>\n");
xml.append("<" + ManagedServices.TAG_MANAGED_SERVICES + " "
+ ManagedServices.ATT_USER_ID + "=\"98\" "
+ ManagedServices.ATT_IS_PRIMARY + "=\"false\" "
- + ManagedServices.ATT_APPROVED_LIST + "=\"981\" />\n");
+ + ManagedServices.ATT_APPROVED_LIST + "=\"981\" "
+ + (mUserSetString != null ? ManagedServices.ATT_USER_SET + "=\""
+ + mUserSetString + "\" " : "")
+ + " />\n");
xml.append("</" + xmlTag + ">");
return xml.toString();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 6722fff..054a401 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
@@ -41,11 +42,13 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.testing.TestableContext;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.TypedXmlPullParser;
import android.util.Xml;
+import com.android.internal.util.function.TriPredicate;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
@@ -132,6 +135,68 @@
}
@Test
+ public void testReadXml_userDisabled() throws Exception {
+ String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
+ + "<service_listing approved=\"\" user=\"0\" primary=\"true\""
+ + "user_changed=\"true\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ TriPredicate<String, Integer, String> allowedManagedServicePackages =
+ mNm::canUseManagedServices;
+
+ parser.nextTag();
+ mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+ ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(0);
+
+ // approved should not be null
+ assertNotNull(approved);
+ assertEquals(new ArraySet<>(), approved.get(true));
+ }
+
+ @Test
+ public void testReadXml_upgradeUserSet() throws Exception {
+ String xml = "<enabled_assistants version=\"3\" defaults=\"b/b\">"
+ + "<service_listing approved=\"\" user=\"0\" primary=\"true\""
+ + "user_set_services=\"b/b\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ TriPredicate<String, Integer, String> allowedManagedServicePackages =
+ mNm::canUseManagedServices;
+
+ parser.nextTag();
+ mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+ verify(mAssistants, times(1)).upgradeUserSet();
+ assertTrue(mAssistants.mIsUserChanged.get(0));
+ }
+
+ @Test
+ public void testReadXml_multiApproved() throws Exception {
+ String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
+ + "<service_listing approved=\"a/a:b/b\" user=\"0\" primary=\"true\""
+ + "user_changed=\"true\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+
+ parser.nextTag();
+ mAssistants.readXml(parser, null, false, UserHandle.USER_ALL);
+
+ assertEquals(1, mAssistants.getAllowedComponents(0).size());
+ assertEquals(new ArrayList(Arrays.asList(new ComponentName("a", "a"))),
+ mAssistants.getAllowedComponents(0));
+ }
+
+ @Test
public void testXmlUpgradeExistingApprovedComponents() throws Exception {
String xml = "<enabled_assistants version=\"2\" defaults=\"b\\b\">"
+ "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index f277e94..6702869 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2777,8 +2777,9 @@
private void grantUsingBackgroundNetworksPermissionForUid(
final int uid, final String packageName) throws Exception {
- when(mPackageManager.getPackageInfo(eq(packageName), eq(GET_PERMISSIONS)))
- .thenReturn(buildPackageInfo(true, uid));
+ when(mPackageManager.getPackageInfo(
+ eq(packageName), eq(GET_PERMISSIONS | MATCH_ANY_USER)))
+ .thenReturn(buildPackageInfo(true /* hasSystemPermission */, uid));
mService.mPermissionMonitor.onPackageAdded(packageName, uid);
}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index d7535a9..02a5808 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -479,13 +479,14 @@
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
- buildPackageInfo(/* SYSTEM */ true, SYSTEM_UID1, MOCK_USER1),
- buildPackageInfo(/* SYSTEM */ false, MOCK_UID1, MOCK_USER1),
- buildPackageInfo(/* SYSTEM */ false, MOCK_UID2, MOCK_USER1),
- buildPackageInfo(/* SYSTEM */ false, VPN_UID, MOCK_USER1)
+ buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
}));
- when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
- buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
+ when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
+ eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring();
// Every app on user 0 except MOCK_UID2 are under VPN.
final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
@@ -530,11 +531,12 @@
public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
- buildPackageInfo(true, SYSTEM_UID1, MOCK_USER1),
- buildPackageInfo(false, VPN_UID, MOCK_USER1)
+ buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
}));
- when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
- buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
+ when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
+ eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring();
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 61ba09b..f2c6b15 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -135,7 +135,8 @@
template <typename Predicate>
void Filter(Predicate&& func) {
children_.erase(std::remove_if(children_.begin(), children_.end(),
- [&](const auto& e) { return func(e.get()); }));
+ [&](const auto& e) { return func(e.get()); }),
+ children_.end());
}
/** Retrieves the list of children of the element. */
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 2ec01cd..72eaa35 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -255,7 +255,7 @@
break;
case android::RES_TABLE_STAGED_ALIAS_TYPE:
- if (!ParseOverlayable(parser.chunk())) {
+ if (!ParseStagedAliases(parser.chunk())) {
return false;
}
break;
@@ -518,7 +518,7 @@
return false;
}
- // Set the staged if of the finalized resource.
+ // Set the staged id of the finalized resource.
const auto& resource_name = iter->second;
const StagedId staged_id_def{.id = staged_id};
if (!table_->AddResource(NewResourceBuilder(resource_name)
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index 7cfa784..9ceb204 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -43,8 +43,8 @@
* cccc dd
*/
fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String {
- val col1w = map { (a, _) -> a.length }.max()!!
- val col2w = map { (_, b) -> b.length }.max()!!
+ val col1w = map { (a, _) -> a.length }.maxOrNull()!!
+ val col2w = map { (_, b) -> b.length }.maxOrNull()!!
return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n")
}