diff options
1312 files changed, 25603 insertions, 18967 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index c6ce799f0a24..1ae9ada41338 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -81,6 +81,7 @@ aconfig_declarations_group { "android.view.inputmethod.flags-aconfig-java", "android.webkit.flags-aconfig-java", "android.widget.flags-aconfig-java", + "android.xr.flags-aconfig-java", "art_exported_aconfig_flags_lib", "backstage_power_flags_lib", "backup_flags_lib", @@ -109,6 +110,7 @@ aconfig_declarations_group { "hwui_flags_java_lib", "interaction_jank_monitor_flags_lib", "libcore_exported_aconfig_flags_lib", + "libcore_readonly_aconfig_flags_lib", "libgui_flags_java_lib", "power_flags_lib", "sdk_sandbox_flags_lib", @@ -168,6 +170,26 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// See b/368409430 - This is for libcore flags to be generated with +// force-read-only mode, so access to the flags does not involve I/O, +// which could break Isolated Processes with I/O permission disabled. +// The issue will be addressed once new Aconfig storage API is landed +// and the readonly version will be removed. +aconfig_declarations { + name: "libcore-readonly-aconfig-flags", + package: "com.android.libcore.readonly", + container: "system", + srcs: ["libcore-readonly.aconfig"], +} + +// Core Libraries / libcore +java_aconfig_library { + name: "libcore_readonly_aconfig_flags_lib", + aconfig_declarations: "libcore-readonly-aconfig-flags", + mode: "force-read-only", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Telecom java_aconfig_library { name: "telecom_flags_core_java_lib", @@ -791,6 +813,12 @@ java_aconfig_library { ], } +cc_aconfig_library { + name: "android.permission.flags-aconfig-cc", + aconfig_declarations: "android.permission.flags-aconfig", + host_supported: true, +} + // SQLite aconfig_declarations { name: "android.database.sqlite-aconfig", @@ -893,6 +921,20 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// XR +aconfig_declarations { + name: "android.xr.flags-aconfig", + package: "android.xr", + container: "system", + srcs: ["core/java/android/content/pm/xr.aconfig"], +} + +java_aconfig_library { + name: "android.xr.flags-aconfig-java", + aconfig_declarations: "android.xr.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // android.app aconfig_declarations { name: "android.app.flags-aconfig", diff --git a/apct-tests/perftests/core/src/android/os/MessageQueuePerfTest.java b/apct-tests/perftests/core/src/android/os/MessageQueuePerfTest.java new file mode 100644 index 000000000000..b14de836fa78 --- /dev/null +++ b/apct-tests/perftests/core/src/android/os/MessageQueuePerfTest.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.app.Activity; +import android.app.Instrumentation; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; +import android.perftests.utils.Stats; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.Random; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Performance tests for {@link MessageQueue}. + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class MessageQueuePerfTest { + static final String TAG = "MessageQueuePerfTest"; + private static final int PER_THREAD_MESSAGE_COUNT = 1000; + private static final int THREAD_COUNT = 8; + private static final int TOTAL_MESSAGE_COUNT = PER_THREAD_MESSAGE_COUNT * THREAD_COUNT; + + static Object sLock = new Object(); + private ArrayList<Long> mResults; + + @Before + public void setUp() { } + + @After + public void tearDown() { } + + class EnqueueThread extends Thread { + CountDownLatch mStartLatch; + CountDownLatch mEndLatch; + Handler mHandler; + int mMessageStartIdx; + Message[] mMessages; + long[] mDelays; + + EnqueueThread(CountDownLatch startLatch, CountDownLatch endLatch, Handler handler, + int startIdx, Message[] messages, long[] delays) { + super(); + mStartLatch = startLatch; + mEndLatch = endLatch; + mHandler = handler; + mMessageStartIdx = startIdx; + mMessages = messages; + mDelays = delays; + } + + @Override + public void run() { + Log.d(TAG, "Enqueue thread started at message index " + mMessageStartIdx); + try { + mStartLatch.await(); + } catch (InterruptedException e) { + + } + long now = SystemClock.uptimeMillis(); + long startTimeNS = SystemClock.elapsedRealtimeNanos(); + for (int i = mMessageStartIdx; i < (mMessageStartIdx + PER_THREAD_MESSAGE_COUNT); i++) { + if (mDelays[i] == 0) { + mHandler.sendMessageAtFrontOfQueue(mMessages[i]); + } else { + mHandler.sendMessageAtTime(mMessages[i], now + mDelays[i]); + } + } + long endTimeNS = SystemClock.elapsedRealtimeNanos(); + + synchronized (sLock) { + mResults.add(endTimeNS - startTimeNS); + } + mEndLatch.countDown(); + } + } + + class TestHandler extends Handler { + TestHandler(Looper looper) { + super(looper); + } + + public void handleMessage(Message msg) { } + } + + void reportPerf(String prefix, int threadCount, int perThreadMessageCount) { + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + Stats stats = new Stats(mResults); + + Log.d(TAG, "Reporting perf now"); + + Bundle status = new Bundle(); + status.putLong(prefix + "_median_ns", stats.getMedian()); + status.putLong(prefix + "_mean_ns", (long) stats.getMean()); + status.putLong(prefix + "_min_ns", stats.getMin()); + status.putLong(prefix + "_max_ns", stats.getMax()); + status.putLong(prefix + "_stddev_ns", (long) stats.getStandardDeviation()); + status.putLong(prefix + "_nr_threads", threadCount); + status.putLong(prefix + "_msgs_per_thread", perThreadMessageCount); + instrumentation.sendStatus(Activity.RESULT_OK, status); + } + + HandlerThread mHandlerThread; + + private void fillMessagesArray(Message[] messages) { + for (int i = 0; i < messages.length; i++) { + messages[i] = mHandlerThread.getThreadHandler().obtainMessage(i); + } + } + + private void startTestAndWaitOnThreads(CountDownLatch threadStartLatch, CountDownLatch threadEndLatch) { + try { + threadStartLatch.countDown(); + Log.e(TAG, "Test threads started"); + threadEndLatch.await(); + } catch (InterruptedException ignored) { + } + Log.e(TAG, "Test threads ended, quitting handler thread"); + } + + @Test + public void benchmarkEnqueueAtFrontOfQueue() { + CountDownLatch threadStartLatch = new CountDownLatch(1); + CountDownLatch threadEndLatch = new CountDownLatch(THREAD_COUNT); + mHandlerThread = new HandlerThread("MessageQueuePerfTest"); + mHandlerThread.start(); + Message[] messages = new Message[TOTAL_MESSAGE_COUNT]; + fillMessagesArray(messages); + + long[] delays = new long[TOTAL_MESSAGE_COUNT]; + mResults = new ArrayList<>(); + + TestHandler handler = new TestHandler(mHandlerThread.getLooper()); + for (int i = 0; i < THREAD_COUNT; i++) { + EnqueueThread thread = new EnqueueThread(threadStartLatch, threadEndLatch, handler, + i * PER_THREAD_MESSAGE_COUNT, messages, delays); + thread.start(); + } + + startTestAndWaitOnThreads(threadStartLatch, threadEndLatch); + + mHandlerThread.quitSafely(); + + reportPerf("enqueueAtFront", THREAD_COUNT, PER_THREAD_MESSAGE_COUNT); + } + + /** + * Fill array with random delays, for benchmarkEnqueueDelayed + */ + public long[] fillDelayArray() { + long[] delays = new long[TOTAL_MESSAGE_COUNT]; + Random rand = new Random(0xDEADBEEF); + for (int i = 0; i < TOTAL_MESSAGE_COUNT; i++) { + delays[i] = Math.abs(rand.nextLong() % 5000); + } + return delays; + } + + @Test + public void benchmarkEnqueueDelayed() { + CountDownLatch threadStartLatch = new CountDownLatch(1); + CountDownLatch threadEndLatch = new CountDownLatch(THREAD_COUNT); + mHandlerThread = new HandlerThread("MessageQueuePerfTest"); + mHandlerThread.start(); + Message[] messages = new Message[TOTAL_MESSAGE_COUNT]; + fillMessagesArray(messages); + + long[] delays = fillDelayArray(); + mResults = new ArrayList<>(); + + TestHandler handler = new TestHandler(mHandlerThread.getLooper()); + for (int i = 0; i < THREAD_COUNT; i++) { + EnqueueThread thread = new EnqueueThread(threadStartLatch, threadEndLatch, handler, + i * PER_THREAD_MESSAGE_COUNT, messages, delays); + thread.start(); + } + + startTestAndWaitOnThreads(threadStartLatch, threadEndLatch); + + mHandlerThread.quitSafely(); + + reportPerf("enqueueDelayed", THREAD_COUNT, PER_THREAD_MESSAGE_COUNT); + } +} diff --git a/apex/jobscheduler/service/aconfig/app_idle.aconfig b/apex/jobscheduler/service/aconfig/app_idle.aconfig index c8976ca8361e..f079c02707e0 100644 --- a/apex/jobscheduler/service/aconfig/app_idle.aconfig +++ b/apex/jobscheduler/service/aconfig/app_idle.aconfig @@ -12,3 +12,12 @@ flag { } } +flag { + name: "screen_time_bypass" + namespace: "backstage_power" + description: "Bypass the screen time check for bucket evaluation" + bug: "374114769" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig index 11c5b51e23ae..98e53ab97872 100644 --- a/apex/jobscheduler/service/aconfig/job.aconfig +++ b/apex/jobscheduler/service/aconfig/job.aconfig @@ -82,3 +82,10 @@ flag { description: "Applies the normal quota policy to FGS jobs" bug: "341201311" } + +flag { + name: "adjust_quota_default_constants" + namespace: "backstage_power" + description: "Adjust quota default parameters" + bug: "347058927" +}
\ No newline at end of file diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 03a3a0d51891..885bad5e31c8 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -394,13 +394,13 @@ public final class QuotaController extends StateController { * minutes to run its jobs. */ private final long[] mBucketPeriodsMs = new long[]{ - QcConstants.DEFAULT_WINDOW_SIZE_ACTIVE_MS, - QcConstants.DEFAULT_WINDOW_SIZE_WORKING_MS, - QcConstants.DEFAULT_WINDOW_SIZE_FREQUENT_MS, + QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS, + QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS, + QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS, QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS, 0, // NEVER QcConstants.DEFAULT_WINDOW_SIZE_RESTRICTED_MS, - QcConstants.DEFAULT_WINDOW_SIZE_EXEMPTED_MS + QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS }; /** The maximum period any bucket can have. */ @@ -454,7 +454,7 @@ public final class QuotaController extends StateController { */ private final long[] mEJLimitsMs = new long[]{ QcConstants.DEFAULT_EJ_LIMIT_ACTIVE_MS, - QcConstants.DEFAULT_EJ_LIMIT_WORKING_MS, + QcConstants.DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS, QcConstants.DEFAULT_EJ_LIMIT_FREQUENT_MS, QcConstants.DEFAULT_EJ_LIMIT_RARE_MS, 0, // NEVER @@ -476,7 +476,8 @@ public final class QuotaController extends StateController { /** * Length of time used to split an app's top time into chunks. */ - private long mEJTopAppTimeChunkSizeMs = QcConstants.DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS; + private long mEJTopAppTimeChunkSizeMs = + QcConstants.DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS; /** * How much EJ quota to give back to an app based on the number of top app time chunks it had. @@ -486,7 +487,7 @@ public final class QuotaController extends StateController { /** * How much EJ quota to give back to an app based on each non-top user interaction. */ - private long mEJRewardInteractionMs = QcConstants.DEFAULT_EJ_REWARD_INTERACTION_MS; + private long mEJRewardInteractionMs = QcConstants.DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS; /** * How much EJ quota to give back to an app based on each notification seen event. @@ -498,14 +499,6 @@ public final class QuotaController extends StateController { private long mEJGracePeriodTopAppMs = QcConstants.DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS; - private long mQuotaBumpAdditionalDurationMs = - QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS; - private int mQuotaBumpAdditionalJobCount = QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT; - private int mQuotaBumpAdditionalSessionCount = - QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT; - private long mQuotaBumpWindowSizeMs = QcConstants.DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS; - private int mQuotaBumpLimit = QcConstants.DEFAULT_QUOTA_BUMP_LIMIT; - /** * List of system apps with the {@link android.Manifest.permission#INSTALL_PACKAGES} permission * granted for each user. @@ -578,6 +571,8 @@ public final class QuotaController extends StateController { } catch (RemoteException e) { // ignored; both services live in system_server } + + processQuotaConstantsAdjustment(); } @Override @@ -1095,7 +1090,7 @@ public final class QuotaController extends StateController { // essentially run until they reach the maximum limit. if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) { return calculateTimeUntilQuotaConsumedLocked( - events, startMaxElapsed, maxExecutionTimeRemainingMs, false); + events, startMaxElapsed, maxExecutionTimeRemainingMs); } // Need to check both max time and period time in case one is less than the other. @@ -1104,9 +1099,9 @@ public final class QuotaController extends StateController { // bucket value. return Math.min( calculateTimeUntilQuotaConsumedLocked( - events, startMaxElapsed, maxExecutionTimeRemainingMs, false), + events, startMaxElapsed, maxExecutionTimeRemainingMs), calculateTimeUntilQuotaConsumedLocked( - events, startWindowElapsed, allowedTimeRemainingMs, true)); + events, startWindowElapsed, allowedTimeRemainingMs)); } /** @@ -1116,36 +1111,12 @@ public final class QuotaController extends StateController { * @param deadSpaceMs How much time can be allowed to count towards the quota */ private long calculateTimeUntilQuotaConsumedLocked(@NonNull List<TimedEvent> sessions, - final long windowStartElapsed, long deadSpaceMs, boolean allowQuotaBumps) { + final long windowStartElapsed, long deadSpaceMs) { long timeUntilQuotaConsumedMs = 0; long start = windowStartElapsed; - int numQuotaBumps = 0; - final long quotaBumpWindowStartElapsed = - sElapsedRealtimeClock.millis() - mQuotaBumpWindowSizeMs; final int numSessions = sessions.size(); - if (allowQuotaBumps) { - for (int i = numSessions - 1; i >= 0; --i) { - TimedEvent event = sessions.get(i); - - if (event instanceof QuotaBump) { - if (event.getEndTimeElapsed() >= quotaBumpWindowStartElapsed - && numQuotaBumps++ < mQuotaBumpLimit) { - deadSpaceMs += mQuotaBumpAdditionalDurationMs; - } else { - break; - } - } - } - } for (int i = 0; i < numSessions; ++i) { - TimedEvent event = sessions.get(i); - - if (event instanceof QuotaBump) { - continue; - } - - TimingSession session = (TimingSession) event; - + TimingSession session = (TimingSession) sessions.get(i); if (session.endTimeElapsed < windowStartElapsed) { // Outside of window. Ignore. continue; @@ -1330,41 +1301,15 @@ public final class QuotaController extends StateController { final long startWindowElapsed = nowElapsed - stats.windowSizeMs; final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS; int sessionCountInWindow = 0; - int numQuotaBumps = 0; - final long quotaBumpWindowStartElapsed = nowElapsed - mQuotaBumpWindowSizeMs; // The minimum time between the start time and the beginning of the events that were // looked at --> how much time the stats will be valid for. long emptyTimeMs = Long.MAX_VALUE; // Sessions are non-overlapping and in order of occurrence, so iterating backwards will get // the most recent ones. final int loopStart = events.size() - 1; - // Process QuotaBumps first to ensure the limits are properly adjusted. - for (int i = loopStart; i >= 0; --i) { - TimedEvent event = events.get(i); - - if (event.getEndTimeElapsed() < quotaBumpWindowStartElapsed - || numQuotaBumps >= mQuotaBumpLimit) { - break; - } - - if (event instanceof QuotaBump) { - stats.allowedTimePerPeriodMs += mQuotaBumpAdditionalDurationMs; - stats.jobCountLimit += mQuotaBumpAdditionalJobCount; - stats.sessionCountLimit += mQuotaBumpAdditionalSessionCount; - emptyTimeMs = Math.min(emptyTimeMs, - event.getEndTimeElapsed() - quotaBumpWindowStartElapsed); - numQuotaBumps++; - } - } TimingSession lastSeenTimingSession = null; for (int i = loopStart; i >= 0; --i) { - TimedEvent event = events.get(i); - - if (event instanceof QuotaBump) { - continue; - } - - TimingSession session = (TimingSession) event; + TimingSession session = (TimingSession) events.get(i); // Window management. if (startWindowElapsed < session.endTimeElapsed) { @@ -1469,6 +1414,13 @@ public final class QuotaController extends StateController { } } + void processQuotaConstantsAdjustment() { + if (Flags.adjustQuotaDefaultConstants()) { + mQcConstants.adjustDefaultBucketWindowSizes(); + mQcConstants.adjustDefaultEjLimits(); + } + } + @VisibleForTesting void incrementJobCountLocked(final int userId, @NonNull final String packageName, int count) { final long now = sElapsedRealtimeClock.millis(); @@ -2058,28 +2010,6 @@ public final class QuotaController extends StateController { } @VisibleForTesting - static final class QuotaBump implements TimedEvent { - // Event timestamp in elapsed realtime timebase. - public final long eventTimeElapsed; - - QuotaBump(long eventElapsed) { - this.eventTimeElapsed = eventElapsed; - } - - @Override - public long getEndTimeElapsed() { - return eventTimeElapsed; - } - - @Override - public void dump(IndentingPrintWriter pw) { - pw.print("Quota bump @ "); - pw.print(eventTimeElapsed); - pw.println(); - } - } - - @VisibleForTesting static final class ShrinkableDebits { /** The amount of quota remaining. Can be negative if limit changes. */ private long mDebitTally; @@ -2528,21 +2458,6 @@ public final class QuotaController extends StateController { updateStandbyBucket(userId, packageName, bucketIndex); }); } - - @Override - public void triggerTemporaryQuotaBump(String packageName, @UserIdInt int userId) { - synchronized (mLock) { - List<TimedEvent> events = mTimingEvents.get(userId, packageName); - if (events == null || events.size() == 0) { - // If the app hasn't run any jobs, there's no point giving it a quota bump. - return; - } - events.add(new QuotaBump(sElapsedRealtimeClock.millis())); - invalidateAllExecutionStatsLocked(userId, packageName); - } - // Update jobs out of band. - mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget(); - } } @VisibleForTesting @@ -3019,7 +2934,6 @@ public final class QuotaController extends StateController { mQcConstants.mRateLimitingConstantsUpdated = false; mQcConstants.mExecutionPeriodConstantsUpdated = false; mQcConstants.mEJLimitConstantsUpdated = false; - mQcConstants.mQuotaBumpConstantsUpdated = false; } @Override @@ -3046,7 +2960,6 @@ public final class QuotaController extends StateController { private boolean mRateLimitingConstantsUpdated = false; private boolean mExecutionPeriodConstantsUpdated = false; private boolean mEJLimitConstantsUpdated = false; - private boolean mQuotaBumpConstantsUpdated = false; /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */ private static final String QC_CONSTANT_PREFIX = "qc_"; @@ -3194,21 +3107,6 @@ public final class QuotaController extends StateController { @VisibleForTesting static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS = QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS = - QC_CONSTANT_PREFIX + "quota_bump_additional_duration_ms"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT = - QC_CONSTANT_PREFIX + "quota_bump_additional_job_count"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = - QC_CONSTANT_PREFIX + "quota_bump_additional_session_count"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_WINDOW_SIZE_MS = - QC_CONSTANT_PREFIX + "quota_bump_window_size_ms"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_LIMIT = - QC_CONSTANT_PREFIX + "quota_bump_limit"; private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = 10 * 60 * 1000L; // 10 minutes @@ -3224,14 +3122,28 @@ public final class QuotaController extends StateController { 10 * 60 * 1000L; // 10 minutes private static final long DEFAULT_IN_QUOTA_BUFFER_MS = 30 * 1000L; // 30 seconds - private static final long DEFAULT_WINDOW_SIZE_EXEMPTED_MS = + // Legacy default window size for EXEMPTED bucket + private static final long DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time - private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS = + // Legacy default window size for ACTIVE bucket + private static final long DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time - private static final long DEFAULT_WINDOW_SIZE_WORKING_MS = + // Legacy default window size for WORKING bucket + private static final long DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS = 2 * 60 * 60 * 1000L; // 2 hours - private static final long DEFAULT_WINDOW_SIZE_FREQUENT_MS = + // Legacy default window size for FREQUENT bucket + private static final long DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS = 8 * 60 * 60 * 1000L; // 8 hours + + private static final long DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS = + 20 * 60 * 1000L; // 20 minutes. + private static final long DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS = + 30 * 60 * 1000L; // 30 minutes. + private static final long DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS = + 4 * 60 * 60 * 1000L; // 4 hours + private static final long DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS = + 12 * 60 * 60 * 1000L; // 12 hours + private static final long DEFAULT_WINDOW_SIZE_RARE_MS = 24 * 60 * 60 * 1000L; // 24 hours private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS = @@ -3245,9 +3157,9 @@ public final class QuotaController extends StateController { 75; // 75/window = 450/hr = 1/session private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE = DEFAULT_MAX_JOB_COUNT_EXEMPTED; private static final int DEFAULT_MAX_JOB_COUNT_WORKING = // 120/window = 60/hr = 12/session - (int) (60.0 * DEFAULT_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS); + (int) (60.0 * DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS); private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT = // 200/window = 25/hr = 25/session - (int) (25.0 * DEFAULT_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS); + (int) (25.0 * DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS); private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session (int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS); private static final int DEFAULT_MAX_JOB_COUNT_RESTRICTED = 10; @@ -3268,24 +3180,24 @@ public final class QuotaController extends StateController { // TODO(267949143): set a different limit for headless system apps private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 60 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_ACTIVE_MS = 30 * MINUTE_IN_MILLIS; - private static final long DEFAULT_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS; + private static final long DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS; + private static final long DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS = 15 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_RARE_MS = DEFAULT_EJ_LIMIT_FREQUENT_MS; private static final long DEFAULT_EJ_LIMIT_RESTRICTED_MS = 5 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_ADDITION_SPECIAL_MS = 15 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_ADDITION_INSTALLER_MS = 30 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_WINDOW_SIZE_MS = 24 * HOUR_IN_MILLIS; - private static final long DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS = 30 * SECOND_IN_MILLIS; + private static final long DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS = + 30 * SECOND_IN_MILLIS; + private static final long DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS = + 5 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS; - private static final long DEFAULT_EJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS; + private static final long DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS; + private static final long DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS = 5 * SECOND_IN_MILLIS; private static final long DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS = 0; private static final long DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = 3 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS = 1 * MINUTE_IN_MILLIS; - private static final long DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS = 1 * MINUTE_IN_MILLIS; - private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT = 2; - private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = 1; - private static final long DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS = 8 * HOUR_IN_MILLIS; - private static final int DEFAULT_QUOTA_BUMP_LIMIT = 8; /** * How much time each app in the exempted bucket will have to run jobs within their standby @@ -3332,28 +3244,28 @@ public final class QuotaController extends StateController { * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS} within the past * WINDOW_SIZE_MS. */ - public long WINDOW_SIZE_EXEMPTED_MS = DEFAULT_WINDOW_SIZE_EXEMPTED_MS; + public long WINDOW_SIZE_EXEMPTED_MS = DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS; /** * The quota window size of the particular standby bucket. Apps in this standby bucket are * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_ACTIVE_MS} within the past * WINDOW_SIZE_MS. */ - public long WINDOW_SIZE_ACTIVE_MS = DEFAULT_WINDOW_SIZE_ACTIVE_MS; + public long WINDOW_SIZE_ACTIVE_MS = DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS; /** * The quota window size of the particular standby bucket. Apps in this standby bucket are * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_WORKING_MS} within the past * WINDOW_SIZE_MS. */ - public long WINDOW_SIZE_WORKING_MS = DEFAULT_WINDOW_SIZE_WORKING_MS; + public long WINDOW_SIZE_WORKING_MS = DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS; /** * The quota window size of the particular standby bucket. Apps in this standby bucket are * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_FREQUENT_MS} within the past * WINDOW_SIZE_MS. */ - public long WINDOW_SIZE_FREQUENT_MS = DEFAULT_WINDOW_SIZE_FREQUENT_MS; + public long WINDOW_SIZE_FREQUENT_MS = DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS; /** * The quota window size of the particular standby bucket. Apps in this standby bucket are @@ -3514,7 +3426,7 @@ public final class QuotaController extends StateController { * standby bucket can only have expedited job sessions totalling EJ_LIMIT (without factoring * in any rewards or free EJs). */ - public long EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_WORKING_MS; + public long EJ_LIMIT_WORKING_MS = DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS; /** * The total expedited job session limit of the particular standby bucket. Apps in this @@ -3558,7 +3470,7 @@ public final class QuotaController extends StateController { /** * Length of time used to split an app's top time into chunks. */ - public long EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS; + public long EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS; /** * How much EJ quota to give back to an app based on the number of top app time chunks it @@ -3569,7 +3481,7 @@ public final class QuotaController extends StateController { /** * How much EJ quota to give back to an app based on each non-top user interaction. */ - public long EJ_REWARD_INTERACTION_MS = DEFAULT_EJ_REWARD_INTERACTION_MS; + public long EJ_REWARD_INTERACTION_MS = DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS; /** * How much EJ quota to give back to an app based on each notification seen event. @@ -3587,32 +3499,51 @@ public final class QuotaController extends StateController { */ public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS; - /** - * How much additional session duration to give an app for each accepted quota bump. - */ - public long QUOTA_BUMP_ADDITIONAL_DURATION_MS = DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS; + void adjustDefaultBucketWindowSizes() { + WINDOW_SIZE_EXEMPTED_MS = DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS; + WINDOW_SIZE_ACTIVE_MS = DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS; + WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS; + WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS; - /** - * How many additional regular jobs to give an app for each accepted quota bump. - */ - public int QUOTA_BUMP_ADDITIONAL_JOB_COUNT = DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT; + mBucketPeriodsMs[EXEMPTED_INDEX] = Math.max( + mAllowedTimePerPeriodMs[EXEMPTED_INDEX], + Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS)); + mBucketPeriodsMs[ACTIVE_INDEX] = Math.max( + mAllowedTimePerPeriodMs[ACTIVE_INDEX], + Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS)); + mBucketPeriodsMs[WORKING_INDEX] = Math.max( + mAllowedTimePerPeriodMs[WORKING_INDEX], + Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS)); + mBucketPeriodsMs[FREQUENT_INDEX] = Math.max( + mAllowedTimePerPeriodMs[FREQUENT_INDEX], + Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS)); + } - /** - * How many additional sessions to give an app for each accepted quota bump. - */ - public int QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = - DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT; + void adjustDefaultEjLimits() { + EJ_LIMIT_WORKING_MS = DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS; + EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS; + EJ_REWARD_INTERACTION_MS = DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS; - /** - * The rolling window size within which to accept and apply quota bump events. - */ - public long QUOTA_BUMP_WINDOW_SIZE_MS = DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS; + // The limit must be in the range [15 minutes, active limit]. + mEJLimitsMs[WORKING_INDEX] = Math.max(15 * MINUTE_IN_MILLIS, + Math.min(mEJLimitsMs[ACTIVE_INDEX], EJ_LIMIT_WORKING_MS)); - /** - * The maximum number of quota bumps to accept and apply within the - * {@link #QUOTA_BUMP_WINDOW_SIZE_MS window}. - */ - public int QUOTA_BUMP_LIMIT = DEFAULT_QUOTA_BUMP_LIMIT; + // Limit interaction reward to be in the range [5 seconds, 15 minutes] per event. + mEJRewardInteractionMs = Math.min(15 * MINUTE_IN_MILLIS, + Math.max(5 * SECOND_IN_MILLIS, EJ_REWARD_INTERACTION_MS)); + + // Limit chunking to be in the range [1 millisecond, 15 minutes] per event. + long newChunkSizeMs = Math.min(15 * MINUTE_IN_MILLIS, + Math.max(1, EJ_TOP_APP_TIME_CHUNK_SIZE_MS)); + mEJTopAppTimeChunkSizeMs = newChunkSizeMs; + if (mEJTopAppTimeChunkSizeMs < mEJRewardTopAppMs) { + // Not making chunk sizes and top rewards to be the upper/lower + // limits of the other to allow trying different policies. Just log + // the discrepancy. + Slog.w(TAG, "EJ top app time chunk less than reward: " + + mEJTopAppTimeChunkSizeMs + " vs " + mEJRewardTopAppMs); + } + } public void processConstantLocked(@NonNull DeviceConfig.Properties properties, @NonNull String key) { @@ -3650,14 +3581,6 @@ public final class QuotaController extends StateController { updateEJLimitConstantsLocked(); break; - case KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS: - case KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT: - case KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT: - case KEY_QUOTA_BUMP_WINDOW_SIZE_MS: - case KEY_QUOTA_BUMP_LIMIT: - updateQuotaBumpConstantsLocked(); - break; - case KEY_MAX_JOB_COUNT_EXEMPTED: MAX_JOB_COUNT_EXEMPTED = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_EXEMPTED); int newExemptedMaxJobCount = @@ -3790,7 +3713,9 @@ public final class QuotaController extends StateController { case KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS: // We don't need to re-evaluate execution stats or constraint status for this. EJ_TOP_APP_TIME_CHUNK_SIZE_MS = - properties.getLong(key, DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS); + properties.getLong(key, Flags.adjustQuotaDefaultConstants() + ? DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS : + DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS); // Limit chunking to be in the range [1 millisecond, 15 minutes] per event. long newChunkSizeMs = Math.min(15 * MINUTE_IN_MILLIS, Math.max(1, EJ_TOP_APP_TIME_CHUNK_SIZE_MS)); @@ -3826,7 +3751,9 @@ public final class QuotaController extends StateController { case KEY_EJ_REWARD_INTERACTION_MS: // We don't need to re-evaluate execution stats or constraint status for this. EJ_REWARD_INTERACTION_MS = - properties.getLong(key, DEFAULT_EJ_REWARD_INTERACTION_MS); + properties.getLong(key, Flags.adjustQuotaDefaultConstants() + ? DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS : + DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS); // Limit interaction reward to be in the range [5 seconds, 15 minutes] per // event. mEJRewardInteractionMs = Math.min(15 * MINUTE_IN_MILLIS, @@ -3900,14 +3827,23 @@ public final class QuotaController extends StateController { MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS, DEFAULT_MAX_EXECUTION_TIME_MS); WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS, - DEFAULT_WINDOW_SIZE_EXEMPTED_MS); + Flags.adjustQuotaDefaultConstants() + ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS : + DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS); WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS, - DEFAULT_WINDOW_SIZE_ACTIVE_MS); + Flags.adjustQuotaDefaultConstants() + ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS : + DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS); WINDOW_SIZE_WORKING_MS = - properties.getLong(KEY_WINDOW_SIZE_WORKING_MS, DEFAULT_WINDOW_SIZE_WORKING_MS); + properties.getLong(KEY_WINDOW_SIZE_WORKING_MS, + Flags.adjustQuotaDefaultConstants() + ? DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS : + DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS); WINDOW_SIZE_FREQUENT_MS = properties.getLong(KEY_WINDOW_SIZE_FREQUENT_MS, - DEFAULT_WINDOW_SIZE_FREQUENT_MS); + Flags.adjustQuotaDefaultConstants() + ? DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS : + DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS); WINDOW_SIZE_RARE_MS = properties.getLong(KEY_WINDOW_SIZE_RARE_MS, DEFAULT_WINDOW_SIZE_RARE_MS); WINDOW_SIZE_RESTRICTED_MS = @@ -4078,7 +4014,9 @@ public final class QuotaController extends StateController { EJ_LIMIT_ACTIVE_MS = properties.getLong( KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS); EJ_LIMIT_WORKING_MS = properties.getLong( - KEY_EJ_LIMIT_WORKING_MS, DEFAULT_EJ_LIMIT_WORKING_MS); + KEY_EJ_LIMIT_WORKING_MS, Flags.adjustQuotaDefaultConstants() + ? DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS : + DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS); EJ_LIMIT_FREQUENT_MS = properties.getLong( KEY_EJ_LIMIT_FREQUENT_MS, DEFAULT_EJ_LIMIT_FREQUENT_MS); EJ_LIMIT_RARE_MS = properties.getLong( @@ -4156,65 +4094,6 @@ public final class QuotaController extends StateController { } } - private void updateQuotaBumpConstantsLocked() { - if (mQuotaBumpConstantsUpdated) { - return; - } - mQuotaBumpConstantsUpdated = true; - - // Query the values as an atomic set. - final DeviceConfig.Properties properties = DeviceConfig.getProperties( - DeviceConfig.NAMESPACE_JOB_SCHEDULER, - KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, - KEY_QUOTA_BUMP_WINDOW_SIZE_MS, KEY_QUOTA_BUMP_LIMIT); - QUOTA_BUMP_ADDITIONAL_DURATION_MS = properties.getLong( - KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS); - QUOTA_BUMP_ADDITIONAL_JOB_COUNT = properties.getInt( - KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT); - QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = properties.getInt( - KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, - DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT); - QUOTA_BUMP_WINDOW_SIZE_MS = properties.getLong( - KEY_QUOTA_BUMP_WINDOW_SIZE_MS, DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS); - QUOTA_BUMP_LIMIT = properties.getInt( - KEY_QUOTA_BUMP_LIMIT, DEFAULT_QUOTA_BUMP_LIMIT); - - // The window must be in the range [1 hour, 24 hours]. - long newWindowSizeMs = Math.max(HOUR_IN_MILLIS, - Math.min(MAX_PERIOD_MS, QUOTA_BUMP_WINDOW_SIZE_MS)); - if (mQuotaBumpWindowSizeMs != newWindowSizeMs) { - mQuotaBumpWindowSizeMs = newWindowSizeMs; - mShouldReevaluateConstraints = true; - } - // The limit must be nonnegative. - int newLimit = Math.max(0, QUOTA_BUMP_LIMIT); - if (mQuotaBumpLimit != newLimit) { - mQuotaBumpLimit = newLimit; - mShouldReevaluateConstraints = true; - } - // The job count must be nonnegative. - int newJobAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_JOB_COUNT); - if (mQuotaBumpAdditionalJobCount != newJobAddition) { - mQuotaBumpAdditionalJobCount = newJobAddition; - mShouldReevaluateConstraints = true; - } - // The session count must be nonnegative. - int newSessionAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_SESSION_COUNT); - if (mQuotaBumpAdditionalSessionCount != newSessionAddition) { - mQuotaBumpAdditionalSessionCount = newSessionAddition; - mShouldReevaluateConstraints = true; - } - // The additional duration must be in the range [0, 10 minutes]. - long newAdditionalDuration = Math.max(0, - Math.min(10 * MINUTE_IN_MILLIS, QUOTA_BUMP_ADDITIONAL_DURATION_MS)); - if (mQuotaBumpAdditionalDurationMs != newAdditionalDuration) { - mQuotaBumpAdditionalDurationMs = newAdditionalDuration; - mShouldReevaluateConstraints = true; - } - } - private void dump(IndentingPrintWriter pw) { pw.println(); pw.println("QuotaController:"); @@ -4277,15 +4156,6 @@ public final class QuotaController extends StateController { EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS).println(); pw.print(KEY_EJ_GRACE_PERIOD_TOP_APP_MS, EJ_GRACE_PERIOD_TOP_APP_MS).println(); - pw.print(KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - QUOTA_BUMP_ADDITIONAL_DURATION_MS).println(); - pw.print(KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, - QUOTA_BUMP_ADDITIONAL_JOB_COUNT).println(); - pw.print(KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, - QUOTA_BUMP_ADDITIONAL_SESSION_COUNT).println(); - pw.print(KEY_QUOTA_BUMP_WINDOW_SIZE_MS, QUOTA_BUMP_WINDOW_SIZE_MS).println(); - pw.print(KEY_QUOTA_BUMP_LIMIT, QUOTA_BUMP_LIMIT).println(); - pw.decreaseIndent(); } @@ -4503,37 +4373,19 @@ public final class QuotaController extends StateController { return mQcConstants; } - @VisibleForTesting - long getQuotaBumpAdditionDurationMs() { - return mQuotaBumpAdditionalDurationMs; - } - - @VisibleForTesting - int getQuotaBumpAdditionJobCount() { - return mQuotaBumpAdditionalJobCount; - } - - @VisibleForTesting - int getQuotaBumpAdditionSessionCount() { - return mQuotaBumpAdditionalSessionCount; - } - - @VisibleForTesting - int getQuotaBumpLimit() { - return mQuotaBumpLimit; - } - - @VisibleForTesting - long getQuotaBumpWindowSizeMs() { - return mQuotaBumpWindowSizeMs; - } - //////////////////////////// DATA DUMP ////////////////////////////// @NeverCompile // Avoid size overhead of debugging code. @Override public void dumpControllerStateLocked(final IndentingPrintWriter pw, final Predicate<JobStatus> predicate) { + pw.println("Flags: "); + pw.println(" " + Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS + + ": " + Flags.adjustQuotaDefaultConstants()); + pw.println(" " + Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS + + ": " + Flags.enforceQuotaPolicyToFgsJobs()); + pw.println(); + pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis()); pw.println(); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java index 6265d9bb815f..a8641ae43509 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java @@ -629,14 +629,15 @@ public class AppIdleHistory { } /** - * Returns the index in the arrays of screenTimeThresholds and elapsedTimeThresholds - * that corresponds to how long since the app was used. + * Returns the index in the array of elapsedTimeThresholds that corresponds to + * how long since the app was used. * @param packageName * @param userId * @param elapsedRealtime current time - * @param screenTimeThresholds Array of screen times, in ascending order, first one is 0 + * @param screenTimeThresholds Array of screen times, in ascending order, + * first one is 0 (this will not be used any more) * @param elapsedTimeThresholds Array of elapsed time, in ascending order, first one is 0 - * @return The index whose values the app's used time exceeds (in both arrays) or {@code -1} to + * @return The index whose values the app's used time exceeds or {@code -1} to * indicate that the app has never been used. */ int getThresholdIndex(String packageName, int userId, long elapsedRealtime, @@ -646,7 +647,7 @@ public class AppIdleHistory { elapsedRealtime, false); // If we don't have any state for the app, assume never used if (appUsageHistory == null || appUsageHistory.lastUsedElapsedTime < 0 - || appUsageHistory.lastUsedScreenTime < 0) { + || (!Flags.screenTimeBypass() && appUsageHistory.lastUsedScreenTime < 0)) { return -1; } @@ -659,7 +660,7 @@ public class AppIdleHistory { if (DEBUG) Slog.d(TAG, packageName + " screenOn=" + screenOnDelta + ", elapsed=" + elapsedDelta); for (int i = screenTimeThresholds.length - 1; i >= 0; i--) { - if (screenOnDelta >= screenTimeThresholds[i] + if ((Flags.screenTimeBypass() || screenOnDelta >= screenTimeThresholds[i]) && elapsedDelta >= elapsedTimeThresholds[i]) { return i; } diff --git a/core/api/current.txt b/core/api/current.txt index 035f82388e6e..012a2e62755c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -239,6 +239,7 @@ package android { field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"; field public static final String PROVIDE_OWN_AUTOFILL_SUGGESTIONS = "android.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS"; field public static final String PROVIDE_REMOTE_CREDENTIALS = "android.permission.PROVIDE_REMOTE_CREDENTIALS"; + field @FlaggedApi("android.security.aapm_api") public static final String QUERY_ADVANCED_PROTECTION_MODE = "android.permission.QUERY_ADVANCED_PROTECTION_MODE"; field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES"; field public static final String READ_ASSISTANT_APP_SEARCH_DATA = "android.permission.READ_ASSISTANT_APP_SEARCH_DATA"; field public static final String READ_BASIC_PHONE_STATE = "android.permission.READ_BASIC_PHONE_STATE"; @@ -8781,7 +8782,6 @@ package android.app.admin { package android.app.appfunctions { @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager { - method @Deprecated @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>); method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>); method public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>); method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>); @@ -8793,9 +8793,7 @@ package android.app.appfunctions { @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service { ctor public AppFunctionService(); method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); - method @Deprecated @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>); - method @Deprecated @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>); - method @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>); + method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>); field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService"; } @@ -10804,6 +10802,7 @@ package android.content { field public static final String ACCESSIBILITY_SERVICE = "accessibility"; field public static final String ACCOUNT_SERVICE = "account"; field public static final String ACTIVITY_SERVICE = "activity"; + field @FlaggedApi("android.security.aapm_api") public static final String ADVANCED_PROTECTION_SERVICE = "advanced_protection"; field public static final String ALARM_SERVICE = "alarm"; field public static final String APPWIDGET_SERVICE = "appwidget"; field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String APP_FUNCTION_SERVICE = "app_function"; @@ -32880,6 +32879,41 @@ package android.os { } @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static class Build.VERSION_CODES_FULL { + field public static final int BASE = 100000; // 0x186a0 + field public static final int BASE_1_1 = 200000; // 0x30d40 + field public static final int CUPCAKE = 300000; // 0x493e0 + field public static final int DONUT = 400000; // 0x61a80 + field public static final int ECLAIR = 500000; // 0x7a120 + field public static final int ECLAIR_0_1 = 600000; // 0x927c0 + field public static final int ECLAIR_MR1 = 700000; // 0xaae60 + field public static final int FROYO = 800000; // 0xc3500 + field public static final int GINGERBREAD = 900000; // 0xdbba0 + field public static final int GINGERBREAD_MR1 = 1000000; // 0xf4240 + field public static final int HONEYCOMB = 1100000; // 0x10c8e0 + field public static final int HONEYCOMB_MR1 = 1200000; // 0x124f80 + field public static final int HONEYCOMB_MR2 = 1300000; // 0x13d620 + field public static final int ICE_CREAM_SANDWICH = 1400000; // 0x155cc0 + field public static final int ICE_CREAM_SANDWICH_MR1 = 1500000; // 0x16e360 + field public static final int JELLY_BEAN = 1600000; // 0x186a00 + field public static final int JELLY_BEAN_MR1 = 1700000; // 0x19f0a0 + field public static final int JELLY_BEAN_MR2 = 1800000; // 0x1b7740 + field public static final int KITKAT = 1900000; // 0x1cfde0 + field public static final int KITKAT_WATCH = 2000000; // 0x1e8480 + field public static final int LOLLIPOP = 2100000; // 0x200b20 + field public static final int LOLLIPOP_MR1 = 2200000; // 0x2191c0 + field public static final int M = 2300000; // 0x231860 + field public static final int N = 2400000; // 0x249f00 + field public static final int N_MR1 = 2500000; // 0x2625a0 + field public static final int O = 2600000; // 0x27ac40 + field public static final int O_MR1 = 2700000; // 0x2932e0 + field public static final int P = 2800000; // 0x2ab980 + field public static final int Q = 2900000; // 0x2c4020 + field public static final int R = 3000000; // 0x2dc6c0 + field public static final int S = 3100000; // 0x2f4d60 + field public static final int S_V2 = 3200000; // 0x30d400 + field public static final int TIRAMISU = 3300000; // 0x325aa0 + field public static final int UPSIDE_DOWN_CAKE = 3400000; // 0x33e140 + field public static final int VANILLA_ICE_CREAM = 3500000; // 0x3567e0 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { @@ -39699,6 +39733,20 @@ package android.security { } +package android.security.advancedprotection { + + @FlaggedApi("android.security.aapm_api") public class AdvancedProtectionManager { + method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public boolean isAdvancedProtectionEnabled(); + method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public void registerAdvancedProtectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.security.advancedprotection.AdvancedProtectionManager.Callback); + method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public void unregisterAdvancedProtectionCallback(@NonNull android.security.advancedprotection.AdvancedProtectionManager.Callback); + } + + @FlaggedApi("android.security.aapm_api") public static interface AdvancedProtectionManager.Callback { + method public void onAdvancedProtectionChanged(boolean); + } + +} + package android.security.identity { public class AccessControlProfile { @@ -46367,7 +46415,7 @@ package android.telephony { field public static final String ACTION_MULTI_SIM_CONFIG_CHANGED = "android.telephony.action.MULTI_SIM_CONFIG_CHANGED"; field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED"; field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE"; - field @FlaggedApi("com.android.internal.telephony.flags.reset_mobile_network_settings") public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS"; + field public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS"; field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE"; field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE"; field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION"; @@ -52181,7 +52229,7 @@ package android.view { method public void clear(); method public void copyFrom(android.view.MotionEvent.PointerCoords); method public float getAxisValue(int); - method @FlaggedApi("com.android.hardware.input.pointer_coords_is_resampled_api") public boolean isResampled(); + method public boolean isResampled(); method public void setAxisValue(int, float); field public float orientation; field public float pressure; @@ -56601,7 +56649,7 @@ package android.view.inputmethod { method public java.util.Map<android.view.inputmethod.InputMethodInfo,java.util.List<android.view.inputmethod.InputMethodSubtype>> getShortcutInputMethodsAndSubtypes(); method @Deprecated public void hideSoftInputFromInputMethod(android.os.IBinder, int); method public boolean hideSoftInputFromWindow(android.os.IBinder, int); - method public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver); + method @Deprecated public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver); method @Deprecated public void hideStatusIcon(android.os.IBinder); method public void invalidateInput(@NonNull android.view.View); method public boolean isAcceptingText(); @@ -56625,7 +56673,7 @@ package android.view.inputmethod { method public void showInputMethodAndSubtypeEnabler(@Nullable String); method public void showInputMethodPicker(); method public boolean showSoftInput(android.view.View, int); - method public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver); + method @Deprecated public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver); method @Deprecated public void showSoftInputFromInputMethod(android.os.IBinder, int); method @Deprecated public void showStatusIcon(android.os.IBinder, String, @DrawableRes int); method @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public void startConnectionlessStylusHandwriting(@NonNull android.view.View, @Nullable android.view.inputmethod.CursorAnchorInfo, @NonNull java.util.concurrent.Executor, @NonNull android.view.inputmethod.ConnectionlessHandwritingCallback); @@ -58131,12 +58179,16 @@ package android.webkit { method public abstract String[] getAcceptTypes(); method @Nullable public abstract String getFilenameHint(); method public abstract int getMode(); + method @FlaggedApi("android.webkit.file_system_access") public int getPermissionMode(); method @Nullable public abstract CharSequence getTitle(); method public abstract boolean isCaptureEnabled(); method @Nullable public static android.net.Uri[] parseResult(int, android.content.Intent); field public static final int MODE_OPEN = 0; // 0x0 + field @FlaggedApi("android.webkit.file_system_access") public static final int MODE_OPEN_FOLDER = 2; // 0x2 field public static final int MODE_OPEN_MULTIPLE = 1; // 0x1 field public static final int MODE_SAVE = 3; // 0x3 + field @FlaggedApi("android.webkit.file_system_access") public static final int PERMISSION_MODE_READ = 0; // 0x0 + field @FlaggedApi("android.webkit.file_system_access") public static final int PERMISSION_MODE_READ_WRITE = 1; // 0x1 } public abstract class WebHistoryItem implements java.lang.Cloneable { @@ -58493,7 +58545,7 @@ package android.webkit { method public void setWebViewRenderProcessClient(@Nullable android.webkit.WebViewRenderProcessClient); method @Deprecated public boolean shouldDelayChildPressedState(); method @Deprecated public boolean showFindDialog(@Nullable String, boolean); - method public static void startSafeBrowsing(@NonNull android.content.Context, @Nullable android.webkit.ValueCallback<java.lang.Boolean>); + method @Deprecated @FlaggedApi("android.webkit.deprecate_start_safe_browsing") public static void startSafeBrowsing(@NonNull android.content.Context, @Nullable android.webkit.ValueCallback<java.lang.Boolean>); method public void stopLoading(); method public void zoomBy(float); method public boolean zoomIn(); diff --git a/core/api/removed.txt b/core/api/removed.txt index 3c7c0d6e6ea1..a3cab291a1de 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -82,12 +82,12 @@ package android.database { package android.graphics { @Deprecated public class AvoidXfermode extends android.graphics.Xfermode { - ctor public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode); + ctor @Deprecated public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode); } - public enum AvoidXfermode.Mode { - enum_constant public static final android.graphics.AvoidXfermode.Mode AVOID; - enum_constant public static final android.graphics.AvoidXfermode.Mode TARGET; + @Deprecated public enum AvoidXfermode.Mode { + enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode AVOID; + enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode TARGET; } public class Canvas { @@ -102,9 +102,9 @@ package android.graphics { } @Deprecated public class LayerRasterizer extends android.graphics.Rasterizer { - ctor public LayerRasterizer(); - method public void addLayer(android.graphics.Paint, float, float); - method public void addLayer(android.graphics.Paint); + ctor @Deprecated public LayerRasterizer(); + method @Deprecated public void addLayer(android.graphics.Paint, float, float); + method @Deprecated public void addLayer(android.graphics.Paint); } public class Paint { @@ -118,7 +118,7 @@ package android.graphics { } @Deprecated public class PixelXorXfermode extends android.graphics.Xfermode { - ctor public PixelXorXfermode(int); + ctor @Deprecated public PixelXorXfermode(int); } public class Rasterizer { @@ -170,14 +170,14 @@ package android.media.tv { package android.net { @Deprecated public class NetworkBadging { - method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme); - field public static final int BADGING_4K = 30; // 0x1e - field public static final int BADGING_HD = 20; // 0x14 - field public static final int BADGING_NONE = 0; // 0x0 - field public static final int BADGING_SD = 10; // 0xa + method @Deprecated @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme); + field @Deprecated public static final int BADGING_4K = 30; // 0x1e + field @Deprecated public static final int BADGING_HD = 20; // 0x14 + field @Deprecated public static final int BADGING_NONE = 0; // 0x0 + field @Deprecated public static final int BADGING_SD = 10; // 0xa } - @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging { + @Deprecated @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging { } public final class Proxy { @@ -304,14 +304,14 @@ package android.provider { @Deprecated public static final class ContactsContract.RawContacts.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns { field @Deprecated public static final String CONTENT_DIRECTORY = "stream_items"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated public static final class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns { field @Deprecated public static final String PHOTO = "photo"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated protected static interface ContactsContract.StreamItemPhotosColumns { @@ -332,16 +332,16 @@ package android.provider { field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item"; field @Deprecated public static final android.net.Uri CONTENT_URI; field @Deprecated public static final String MAX_ITEMS = "max_items"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated public static final class ContactsContract.StreamItems.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns { field @Deprecated public static final String CONTENT_DIRECTORY = "photo"; field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item_photo"; field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item_photo"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated protected static interface ContactsContract.StreamItemsColumns { @@ -447,14 +447,14 @@ package android.text.style { package android.util { @Deprecated public class FloatMath { - method public static float ceil(float); - method public static float cos(float); - method public static float exp(float); - method public static float floor(float); - method public static float hypot(float, float); - method public static float pow(float, float); - method public static float sin(float); - method public static float sqrt(float); + method @Deprecated public static float ceil(float); + method @Deprecated public static float cos(float); + method @Deprecated public static float exp(float); + method @Deprecated public static float floor(float); + method @Deprecated public static float hypot(float, float); + method @Deprecated public static float pow(float, float); + method @Deprecated public static float sin(float); + method @Deprecated public static float sqrt(float); } } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index fa4fc43c3418..207f4b57e8bf 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -362,6 +362,7 @@ package android { field public static final String SERIAL_PORT = "android.permission.SERIAL_PORT"; field @FlaggedApi("android.security.fsverity_api") public static final String SETUP_FSVERITY = "android.permission.SETUP_FSVERITY"; field public static final String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER"; + field @FlaggedApi("android.security.aapm_api") public static final String SET_ADVANCED_PROTECTION_MODE = "android.permission.SET_ADVANCED_PROTECTION_MODE"; field public static final String SET_CLIP_SOURCE = "android.permission.SET_CLIP_SOURCE"; field public static final String SET_DEFAULT_ACCOUNT_FOR_CONTACTS = "android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"; field public static final String SET_HARMFUL_APP_WARNINGS = "android.permission.SET_HARMFUL_APP_WARNINGS"; @@ -4170,9 +4171,11 @@ package android.content.pm { } public class PackageInstaller { + method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public final int getVerificationPolicy(); method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException; method @FlaggedApi("android.content.pm.read_install_info") @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException; method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean); + method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public final boolean setVerificationPolicy(int); field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL"; field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL"; field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2 @@ -4183,12 +4186,20 @@ package android.content.pm { field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_DELETE_FLAGS = "android.content.pm.extra.DELETE_FLAGS"; field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS"; field @Deprecated public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH"; + field @FlaggedApi("android.content.pm.verification_service") public static final String EXTRA_VERIFICATION_FAILURE_REASON = "android.content.pm.extra.VERIFICATION_FAILURE_REASON"; field public static final int LOCATION_DATA_APP = 0; // 0x0 field public static final int LOCATION_MEDIA_DATA = 2; // 0x2 field public static final int LOCATION_MEDIA_OBB = 1; // 0x1 field public static final int REASON_CONFIRM_PACKAGE_CHANGE = 0; // 0x0 field public static final int REASON_OWNERSHIP_CHANGED = 1; // 0x1 field public static final int REASON_REMIND_OWNERSHIP = 2; // 0x2 + field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE = 1; // 0x1 + field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED = 2; // 0x2 + field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_UNKNOWN = 0; // 0x0 + field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_CLOSED = 3; // 0x3 + field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_OPEN = 1; // 0x1 + field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_WARN = 2; // 0x2 + field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_NONE = 0; // 0x0 } public static class PackageInstaller.InstallInfo { @@ -4635,12 +4646,13 @@ package android.content.pm.verify.pkg { method @NonNull public android.content.pm.SigningInfo getSigningInfo(); method @NonNull public android.net.Uri getStagedPackageUri(); method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long getTimeoutTime(); + method public int getVerificationPolicy(); method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus); method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle); method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(int); + method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(int); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.pkg.VerificationSession> CREATOR; - field public static final int VERIFICATION_INCOMPLETE_NETWORK_LIMITED = 2; // 0x2 field public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; // 0x1 field public static final int VERIFICATION_INCOMPLETE_UNKNOWN = 0; // 0x0 } @@ -12322,6 +12334,14 @@ package android.security { } +package android.security.advancedprotection { + + @FlaggedApi("android.security.aapm_api") public class AdvancedProtectionManager { + method @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean); + } + +} + package android.security.keystore { public class AndroidKeyStoreProvider extends java.security.Provider { diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt index bbfa0ec3f3c2..78b9994e8fa1 100644 --- a/core/api/system-removed.txt +++ b/core/api/system-removed.txt @@ -7,8 +7,8 @@ package android.app { } @Deprecated public abstract static class AppOpsManager.AppOpsCollector extends android.app.AppOpsManager.OnOpNotedCallback { - ctor public AppOpsManager.AppOpsCollector(); - method @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor(); + ctor @Deprecated public AppOpsManager.AppOpsCollector(); + method @Deprecated @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor(); } public class Notification implements android.os.Parcelable { @@ -207,7 +207,7 @@ package android.service.translation { @Deprecated public static interface TranslationService.OnTranslationResultCallback { method @Deprecated public void onError(); - method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse); + method @Deprecated public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse); } } @@ -261,64 +261,64 @@ package android.telephony.ims { } @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable { - method public boolean containsKey(@NonNull String); - method @NonNull public android.os.PersistableBundle copyBundle(); - method public int describeContents(); - method public boolean getBoolean(@NonNull String, boolean); - method public int getInt(@NonNull String, int); - method @Nullable public String getString(@NonNull String); - method public long getVersion(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; - field public static final String IPTYPE_IPV4 = "IPV4"; - field public static final String IPTYPE_IPV6 = "IPV6"; - field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; - field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; - field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; - field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; - field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; - field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; - field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; - field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; - field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; - field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; - field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; - field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; - field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; - field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; - field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; - field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; - field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; - field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; - field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; - field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; - field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; - field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; - field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; - field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; - field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; - field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; - field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; - field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; - field public static final String SIP_TRANSPORT_TCP = "TCP"; - field public static final String SIP_TRANSPORT_UDP = "UDP"; - } - - public static final class SipDelegateImsConfiguration.Builder { - ctor public SipDelegateImsConfiguration.Builder(int); - ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); + method @Deprecated public boolean containsKey(@NonNull String); + method @Deprecated @NonNull public android.os.PersistableBundle copyBundle(); + method @Deprecated public int describeContents(); + method @Deprecated public boolean getBoolean(@NonNull String, boolean); + method @Deprecated public int getInt(@NonNull String, int); + method @Deprecated @Nullable public String getString(@NonNull String); + method @Deprecated public long getVersion(); + method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; + field @Deprecated public static final String IPTYPE_IPV4 = "IPV4"; + field @Deprecated public static final String IPTYPE_IPV6 = "IPV6"; + field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; + field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP"; + field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP"; + } + + @Deprecated public static final class SipDelegateImsConfiguration.Builder { + ctor @Deprecated public SipDelegateImsConfiguration.Builder(int); + ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); } } diff --git a/core/java/Android.bp b/core/java/Android.bp index 1265de1ebb15..d12e1bf77e17 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -19,6 +19,7 @@ filegroup { srcs: [ "**/*.java", "**/*.aidl", + ":systemfeatures-gen-srcs", ":framework-nfc-non-updatable-sources", ":messagequeue-gen", ":ranging_stack_mock_initializer", @@ -214,6 +215,9 @@ filegroup { aidl_interface { name: "android.os.hintmanager_aidl", + defaults: [ + "android.hardware.power-aidl", + ], srcs: [ "android/os/IHintManager.aidl", "android/os/IHintSession.aidl", @@ -231,9 +235,6 @@ aidl_interface { enabled: true, }, }, - imports: [ - "android.hardware.power-V5", - ], } aidl_library { @@ -665,3 +666,29 @@ java_library { } // protolog end + +// Whether to enable read-only system feature codegen. +gen_readonly_feature_apis = select(release_flag("RELEASE_USE_SYSTEM_FEATURE_BUILD_FLAGS"), { + true: "true", + false: "false", + default: "false", +}) + +// Generates com.android.internal.pm.RoSystemFeatures, optionally compiling in +// details about fixed system features defined by build flags. When disabled, +// the APIs are simply passthrough stubs with no meaningful side effects. +genrule { + name: "systemfeatures-gen-srcs", + cmd: "$(location systemfeatures-gen-tool) com.android.internal.pm.RoSystemFeatures " + + // --readonly=false (default) makes the codegen an effective no-op passthrough API. + " --readonly=" + gen_readonly_feature_apis + + // For now, only export "android.hardware.type.*" system features APIs. + // TODO(b/203143243): Use an intermediate soong var that aggregates all declared + // RELEASE_SYSTEM_FEATURE_* declarations into a single arg. + " --feature-apis=AUTOMOTIVE,WATCH,TELEVISION,EMBEDDED,PC" + + " > $(out)", + out: [ + "RoSystemFeatures.java", + ], + tools: ["systemfeatures-gen-tool"], +} diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 7273e64846c0..36fc65a76d53 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1031,7 +1031,9 @@ public class ActivityManager { | PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; /** - * All implicit capabilities. There are capabilities that process automatically have. + * All implicit capabilities. This capability set is currently only used for processes under + * active instrumentation. The intent is to allow CTS tests to always have these capabilities + * so that every test doesn't need to launch FGS. * @hide */ @TestApi diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 3bd121a4a19b..f80121d0c9b6 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -1328,7 +1328,8 @@ public abstract class ActivityManagerInternal { * Add a creator token for all embedded intents (stored as extra) of the given intent. * * @param intent The given intent + * @param creatorPackage the package name of the creator app. * @hide */ - public abstract void addCreatorToken(Intent intent); + public abstract void addCreatorToken(Intent intent, String creatorPackage); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 99625ac20e60..e7f4dbc24022 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -7346,6 +7346,8 @@ public final class ActivityThread extends ClientTransactionHandler } } + VMDebug.setUserId(UserHandle.myUserId()); + VMDebug.addApplication(data.appInfo.packageName); // send up app name; do this *before* waiting for debugger Process.setArgV0(data.processName); android.ddm.DdmHandleAppName.setAppName(data.processName, @@ -7868,9 +7870,20 @@ public final class ActivityThread extends ClientTransactionHandler file.getParentFile().mkdirs(); Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); } + + if (ii.packageName != null) { + VMDebug.addApplication(ii.packageName); + } } private void handleFinishInstrumentationWithoutRestart() { + LoadedApk loadedApk = getApplication().mLoadedApk; + // Only remove instrumentation app if this was not a self-testing app. + if (mInstrumentationPackageName != null && loadedApk != null && !mInstrumentationPackageName + .equals(loadedApk.mPackageName)) { + VMDebug.removeApplication(mInstrumentationPackageName); + } + mInstrumentation.onDestroy(); mInstrumentationPackageName = null; mInstrumentationAppDir = null; @@ -8904,6 +8917,11 @@ public final class ActivityThread extends ClientTransactionHandler return false; } + void addApplication(@NonNull Application app) { + mAllApplications.add(app); + VMDebug.addApplication(app.mLoadedApk.mPackageName); + } + @Override public boolean isInDensityCompatMode() { return mDensityCompatMode; diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 1df8f63aa402..1e45d6fd1674 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1478,7 +1478,7 @@ public final class LoadedApk { + " package " + mPackageName + ": " + e.toString(), e); } } - mActivityThread.mAllApplications.add(app); + mActivityThread.addApplication(app); mApplication = app; if (!allowDuplicateInstances) { synchronized (sApplications) { diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index d363e19bcc19..ba71afb49629 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -97,6 +97,7 @@ per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/serve # Performance per-file PropertyInvalidatedCache.java = file:/PERFORMANCE_OWNERS +per-file performance.aconfig = file:/PERFORMANCE_OWNERS # Pinner per-file pinner-client.aconfig = file:/core/java/android/app/pinner/OWNERS diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index c17da249f322..74817643964d 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -16,25 +16,28 @@ package android.app; +import static android.text.TextUtils.formatSimple; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.FastPrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -42,12 +45,14 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; /** @@ -75,10 +80,15 @@ public class PropertyInvalidatedCache<Query, Result> { public abstract @Nullable R apply(@NonNull Q query); /** - * Return true if a query should not use the cache. The default implementation - * always uses the cache. + * Return true if a query should not use the cache. The default implementation returns true + * if the process UID differs from the calling UID. This is to prevent a binder caller from + * reading a cached value created due to a different binder caller, when processes are + * caching on behalf of other processes. */ public boolean shouldBypassCache(@NonNull Q query) { + if(android.multiuser.Flags.propertyInvalidatedCacheBypassMismatchedUids()) { + return Binder.getCallingUid() != Process.myUid(); + } return false; } }; @@ -224,12 +234,24 @@ public class PropertyInvalidatedCache<Query, Result> { } /** - * Reserved nonce values. Use isReservedNonce() to test for a reserved value. Note - * that all values cause the cache to be skipped. + * Reserved nonce values. Use isReservedNonce() to test for a reserved value. Note that all + * reserved values cause the cache to be skipped. */ + // This is the initial value of all cache keys. It is changed when a cache is invalidated. private static final int NONCE_UNSET = 0; + // This value is used in two ways. First, it is used internally to indicate that the cache is + // disabled for the current query. Secondly, it is used to global disable the cache across the + // entire system. Once a cache is disabled, there is no way to enable it again. The global + // behavior is unused and will likely be removed in the future. private static final int NONCE_DISABLED = 1; + // The cache is corked, which means that clients must act as though the cache is always + // invalid. This is used when the server is processing updates that continuously invalidate + // caches. Rather than issuing individual invalidations (which has a performance penalty), + // the server corks the caches at the start of the process and uncorks at the end of the + // process. private static final int NONCE_CORKED = 2; + // The cache is bypassed for the current query. Unlike UNSET and CORKED, this value is never + // written to global store. private static final int NONCE_BYPASS = 3; private static boolean isReservedNonce(long n) { @@ -237,7 +259,7 @@ public class PropertyInvalidatedCache<Query, Result> { } /** - * The names of the nonces + * The names of the reserved nonces. */ private static final String[] sNonceName = new String[]{ "unset", "disabled", "corked", "bypass" }; @@ -277,32 +299,17 @@ public class PropertyInvalidatedCache<Query, Result> { private static final Object sCorkLock = new Object(); /** - * Record the number of invalidate or cork calls that were nops because the cache was already - * corked. This is static because invalidation is done in a static context. Entries are - * indexed by the cache property. - */ - @GuardedBy("sCorkLock") - private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>(); - - /** - * A map of cache keys that we've "corked". (The values are counts.) When a cache key is - * corked, we skip the cache invalidate when the cache key is in the unset state --- that - * is, when a cache key is corked, an invalidation does not enable the cache if somebody - * else hasn't disabled it. - */ - @GuardedBy("sCorkLock") - private static final HashMap<String, Integer> sCorks = new HashMap<>(); - - /** * A lock for the global list of caches and cache keys. This must never be taken inside mLock * or sCorkLock. */ private static final Object sGlobalLock = new Object(); /** - * A map of cache keys that have been disabled in the local process. When a key is - * disabled locally, existing caches are disabled and the key is saved in this map. - * Future cache instances that use the same key will be disabled in their constructor. + * A map of cache keys that have been disabled in the local process. When a key is disabled + * locally, existing caches are disabled and the key is saved in this map. Future cache + * instances that use the same key will be disabled in their constructor. Note that "disabled" + * means the cache is not used in this process. Invalidation still proceeds normally, because + * the cache may be used in other processes. */ @GuardedBy("sGlobalLock") private static final HashSet<String> sDisabledKeys = new HashSet<>(); @@ -315,14 +322,6 @@ public class PropertyInvalidatedCache<Query, Result> { private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = new WeakHashMap<>(); /** - * Counts of the number of times a cache key was invalidated. Invalidation occurs in a static - * context with no cache object available, so this is a static map. Entries are indexed by - * the cache property. - */ - @GuardedBy("sGlobalLock") - private static final HashMap<String, Long> sInvalidates = new HashMap<>(); - - /** * If sEnabled is false then all cache operations are stubbed out. Set * it to false inside test processes. */ @@ -334,12 +333,6 @@ public class PropertyInvalidatedCache<Query, Result> { private final String mPropertyName; /** - * Handle to the {@code mPropertyName} property, transitioning to non-{@code null} once the - * property exists on the system. - */ - private volatile SystemProperties.Handle mPropertyHandle; - - /** * The name by which this cache is known. This should normally be the * binder call that is being cached, but the constructors default it to * the property name. @@ -369,7 +362,13 @@ public class PropertyInvalidatedCache<Query, Result> { private final LinkedHashMap<Query, Result> mCache; /** - * The last value of the {@code mPropertyHandle} that we observed. + * The nonce handler for this cache. + */ + @GuardedBy("mLock") + private final NonceHandler mNonce; + + /** + * The last nonce value that was observed. */ @GuardedBy("mLock") private long mLastSeenNonce = NONCE_UNSET; @@ -385,6 +384,297 @@ public class PropertyInvalidatedCache<Query, Result> { private final int mMaxEntries; /** + * A class to manage cache keys. There is a single instance of this class for each unique key + * that is shared by all cache instances that use that key. This class is abstract; subclasses + * use different storage mechanisms for the nonces. + */ + private static abstract class NonceHandler { + // The name of the nonce. + final String mName; + + // A lock to synchronize corking and invalidation. + protected final Object mLock = new Object(); + + // Count the number of times the property name was invalidated. + @GuardedBy("mLock") + private int mInvalidated = 0; + + // Count the number of times invalidate or cork calls were nops because the cache was + // already corked. + @GuardedBy("mLock") + private int mCorkedInvalidates = 0; + + // Count the number of corks against this property name. This is not a statistic. It + // increases when the property is corked and decreases when the property is uncorked. + // Invalidation requests are ignored when the cork count is greater than zero. + @GuardedBy("mLock") + private int mCorks = 0; + + // The methods to get and set a nonce from whatever storage is being used. + abstract long getNonce(); + abstract void setNonce(long value); + + NonceHandler(@NonNull String name) { + mName = name; + } + + /** + * Write the invalidation nonce for the property. + */ + void invalidate() { + if (!sEnabled) { + if (DEBUG) { + Log.d(TAG, formatSimple("cache invalidate %s suppressed", mName)); + } + return; + } + + synchronized (mLock) { + if (mCorks > 0) { + if (DEBUG) { + Log.d(TAG, "ignoring invalidation due to cork: " + mName); + } + mCorkedInvalidates++; + return; + } + + final long nonce = getNonce(); + if (nonce == NONCE_DISABLED) { + if (DEBUG) { + Log.d(TAG, "refusing to invalidate disabled cache: " + mName); + } + return; + } + + long newValue; + do { + newValue = NoPreloadHolder.next(); + } while (isReservedNonce(newValue)); + if (DEBUG) { + Log.d(TAG, formatSimple( + "invalidating cache [%s]: [%s] -> [%s]", + mName, nonce, Long.toString(newValue))); + } + // There is a small race with concurrent disables here. A compare-and-exchange + // property operation would be required to eliminate the race condition. + setNonce(newValue); + mInvalidated++; + } + } + + void cork() { + if (!sEnabled) { + if (DEBUG) { + Log.d(TAG, formatSimple("cache corking %s suppressed", mName)); + } + return; + } + + synchronized (mLock) { + int numberCorks = mCorks; + if (DEBUG) { + Log.d(TAG, formatSimple( + "corking %s: numberCorks=%s", mName, numberCorks)); + } + + // If we're the first ones to cork this cache, set the cache to the corked state so + // existing caches talk directly to their services while we've corked updates. + // Make sure we don't clobber a disabled cache value. + + // TODO: we can skip this property write and leave the cache enabled if the + // caller promises not to make observable changes to the cache backing state before + // uncorking the cache, e.g., by holding a read lock across the cork-uncork pair. + // Implement this more dangerous mode of operation if necessary. + if (numberCorks == 0) { + final long nonce = getNonce(); + if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) { + setNonce(NONCE_CORKED); + } + } else { + mCorkedInvalidates++; + } + mCorks++; + if (DEBUG) { + Log.d(TAG, "corked: " + mName); + } + } + } + + void uncork() { + if (!sEnabled) { + if (DEBUG) { + Log.d(TAG, formatSimple("cache uncorking %s suppressed", mName)); + } + return; + } + + synchronized (mLock) { + int numberCorks = --mCorks; + if (DEBUG) { + Log.d(TAG, formatSimple( + "uncorking %s: numberCorks=%s", mName, numberCorks)); + } + + if (numberCorks < 0) { + throw new AssertionError("cork underflow: " + mName); + } + if (numberCorks == 0) { + // The property is fully uncorked and can be invalidated normally. + invalidate(); + if (DEBUG) { + Log.d(TAG, "uncorked: " + mName); + } + } + } + } + + void disable() { + if (!sEnabled) { + return; + } + synchronized (mLock) { + setNonce(NONCE_DISABLED); + } + } + + record Stats(int invalidated, int corkedInvalidates) {} + Stats getStats() { + synchronized (mLock) { + return new Stats(mInvalidated, mCorkedInvalidates); + } + } + } + + /** + * Manage nonces that are stored in a system property. + */ + private static final class NonceSysprop extends NonceHandler { + // A handle to the property, for fast lookups. + private volatile SystemProperties.Handle mHandle; + + NonceSysprop(@NonNull String name) { + super(name); + } + + @Override + long getNonce() { + if (mHandle == null) { + synchronized (mLock) { + mHandle = SystemProperties.find(mName); + if (mHandle == null) { + return NONCE_UNSET; + } + } + } + return mHandle.getLong(NONCE_UNSET); + } + + @Override + void setNonce(long value) { + // Failing to set the nonce is a fatal error. Failures setting a system property have + // been reported; given that the failure is probably transient, this function includes + // a retry. + final String str = Long.toString(value); + RuntimeException failure = null; + for (int attempt = 0; attempt < PROPERTY_FAILURE_RETRY_LIMIT; attempt++) { + try { + SystemProperties.set(mName, str); + if (attempt > 0) { + // This log is not guarded. Based on known bug reports, it should + // occur once a week or less. The purpose of the log message is to + // identify the retries as a source of delay that might be otherwise + // be attributed to the cache itself. + Log.w(TAG, "Nonce set after " + attempt + " tries"); + } + return; + } catch (RuntimeException e) { + if (failure == null) { + failure = e; + } + try { + Thread.sleep(PROPERTY_FAILURE_RETRY_DELAY_MILLIS); + } catch (InterruptedException x) { + // Ignore this exception. The desired delay is only approximate and + // there is no issue if the sleep sometimes terminates early. + } + } + } + // This point is reached only if SystemProperties.set() fails at least once. + // Rethrow the first exception that was received. + throw failure; + } + } + + /** + * SystemProperties and shared storage are protected and cannot be written by random + * processes. So, for testing purposes, the NonceTest handler stores the nonce locally. + */ + private static class NonceTest extends NonceHandler { + // The saved nonce. + private long mValue; + + // If this flag is false, the handler has been shutdown during a test. Access to the + // handler in this state is an error. + private boolean mIsActive = true; + + NonceTest(@NonNull String name) { + super(name); + } + + void shutdown() { + // The handler has been discarded as part of test cleanup. Further access is an + // error. + mIsActive = false; + } + + @Override + long getNonce() { + if (!mIsActive) { + throw new IllegalStateException("handler " + mName + " is shutdown"); + } + return mValue; + } + + @Override + void setNonce(long value) { + if (!mIsActive) { + throw new IllegalStateException("handler " + mName + " is shutdown"); + } + mValue = value; + } + } + + /** + * A static list of nonce handlers, indexed by name. NonceHandlers can be safely shared by + * multiple threads, and can therefore be shared by multiple instances of the same cache, and + * with static calls (see {@link #invalidateCache}. Addition and removal are guarded by the + * global lock, to ensure that duplicates are not created. + */ + private static final ConcurrentHashMap<String, NonceHandler> sHandlers + = new ConcurrentHashMap<>(); + + /** + * Return the proper nonce handler, based on the property name. + */ + private static NonceHandler getNonceHandler(@NonNull String name) { + NonceHandler h = sHandlers.get(name); + if (h == null) { + synchronized (sGlobalLock) { + h = sHandlers.get(name); + if (h == null) { + if (name.startsWith("cache_key.test.")) { + h = new NonceTest(name); + } else { + h = new NonceSysprop(name); + } + sHandlers.put(name, h); + } + } + } + return h; + } + + /** * Make a new property invalidated cache. This constructor names the cache after the * property name. New clients should prefer the constructor that takes an explicit * cache name. @@ -417,6 +707,7 @@ public class PropertyInvalidatedCache<Query, Result> { mPropertyName = propertyName; validateCacheKey(mPropertyName); mCacheName = cacheName; + mNonce = getNonceHandler(mPropertyName); mMaxEntries = maxEntries; mComputer = new DefaultComputer<>(this); mCache = createMap(); @@ -441,6 +732,7 @@ public class PropertyInvalidatedCache<Query, Result> { mPropertyName = createPropertyName(module, api); validateCacheKey(mPropertyName); mCacheName = cacheName; + mNonce = getNonceHandler(mPropertyName); mMaxEntries = maxEntries; mComputer = computer; mCache = createMap(); @@ -484,130 +776,58 @@ public class PropertyInvalidatedCache<Query, Result> { } /** - * SystemProperties are protected and cannot be written (or read, usually) by random - * processes. So, for testing purposes, the methods have a bypass mode that reads and - * writes to a HashMap and does not go out to the SystemProperties at all. - */ - - // If true, the cache might be under test. If false, there is no testing in progress. - private static volatile boolean sTesting = false; - - // If sTesting is true then keys that are under test are in this map. - private static final HashMap<String, Long> sTestingPropertyMap = new HashMap<>(); - - /** - * Enable or disable testing. The testing property map is cleared every time this - * method is called. + * Enable or disable testing. At this time, no action is taken when testing begins. * @hide */ @TestApi public static void setTestMode(boolean mode) { - sTesting = mode; - synchronized (sTestingPropertyMap) { - sTestingPropertyMap.clear(); - } - } - - /** - * Enable testing the specific cache key. Only keys in the map are subject to testing. - * There is no method to stop testing a property name. Just disable the test mode. - */ - private static void testPropertyName(@NonNull String name) { - synchronized (sTestingPropertyMap) { - sTestingPropertyMap.put(name, (long) NONCE_UNSET); + if (mode) { + // No action when testing begins. + } else { + resetAfterTest(); } } /** - * Enable testing the specific cache key. Only keys in the map are subject to testing. - * There is no method to stop testing a property name. Just disable the test mode. + * Enable testing the specific cache key. This is a legacy API that will be removed as part of + * b/360897450. * @hide */ @TestApi public void testPropertyName() { - testPropertyName(mPropertyName); - } - - // Read the system property associated with the current cache. This method uses the - // handle for faster reading. - private long getCurrentNonce() { - if (sTesting) { - synchronized (sTestingPropertyMap) { - Long n = sTestingPropertyMap.get(mPropertyName); - if (n != null) { - return n; - } - } - } - - SystemProperties.Handle handle = mPropertyHandle; - if (handle == null) { - handle = SystemProperties.find(mPropertyName); - if (handle == null) { - return NONCE_UNSET; - } - mPropertyHandle = handle; - } - return handle.getLong(NONCE_UNSET); } - // Write the nonce in a static context. No handle is available. - private static void setNonce(String name, long val) { - if (sTesting) { - synchronized (sTestingPropertyMap) { - Long n = sTestingPropertyMap.get(name); - if (n != null) { - sTestingPropertyMap.put(name, val); - return; - } - } - } - RuntimeException failure = null; - for (int attempt = 0; attempt < PROPERTY_FAILURE_RETRY_LIMIT; attempt++) { - try { - SystemProperties.set(name, Long.toString(val)); - if (attempt > 0) { - // This log is not guarded. Based on known bug reports, it should - // occur once a week or less. The purpose of the log message is to - // identify the retries as a source of delay that might be otherwise - // be attributed to the cache itself. - Log.w(TAG, "Nonce set after " + attempt + " tries"); - } - return; - } catch (RuntimeException e) { - if (failure == null) { - failure = e; - } - try { - Thread.sleep(PROPERTY_FAILURE_RETRY_DELAY_MILLIS); - } catch (InterruptedException x) { - // Ignore this exception. The desired delay is only approximate and - // there is no issue if the sleep sometimes terminates early. + /** + * Clean up when testing ends. All NonceTest handlers are erased from the global list and are + * poisoned, just in case the test program has retained a handle to one of the associated + * caches. + * @hide + */ + @VisibleForTesting + public static void resetAfterTest() { + synchronized (sGlobalLock) { + for (Iterator<String> e = sHandlers.keys().asIterator(); e.hasNext(); ) { + String s = e.next(); + final NonceHandler h = sHandlers.get(s); + if (h instanceof NonceTest t) { + t.shutdown(); + sHandlers.remove(s); } } } - // This point is reached only if SystemProperties.set() fails at least once. - // Rethrow the first exception that was received. - throw failure; } - // Set the nonce in a static context. No handle is available. - private static long getNonce(String name) { - if (sTesting) { - synchronized (sTestingPropertyMap) { - Long n = sTestingPropertyMap.get(name); - if (n != null) { - return n; - } - } - } - return SystemProperties.getLong(name, NONCE_UNSET); + // Read the nonce associated with the current cache. + @GuardedBy("mLock") + private long getCurrentNonce() { + return mNonce.getNonce(); } /** - * Forget all cached values. - * TODO(216112648) remove this as a public API. Clients should invalidate caches, not clear - * them. + * Forget all cached values. This is used by a client when the server exits. Since the + * server has exited, the cache values are no longer valid, but the server is no longer + * present to invalidate the cache. Note that this is not necessary if the server is + * system_server, because the entire operating system reboots if that process exits. * @hide */ public final void clear() { @@ -674,7 +894,7 @@ public class PropertyInvalidatedCache<Query, Result> { } /** - * Disable the use of this cache in this process. This method is using internally and during + * Disable the use of this cache in this process. This method is used internally and during * testing. To disable a cache in normal code, use disableLocal(). A disabled cache cannot * be re-enabled. * @hide @@ -783,7 +1003,7 @@ public class PropertyInvalidatedCache<Query, Result> { if (DEBUG) { if (!mDisabled) { - Log.d(TAG, TextUtils.formatSimple( + Log.d(TAG, formatSimple( "cache %s %s for %s", cacheName(), sNonceName[(int) currentNonce], queryToString(query))); } @@ -798,7 +1018,7 @@ public class PropertyInvalidatedCache<Query, Result> { if (cachedResult != null) mHits++; } else { if (DEBUG) { - Log.d(TAG, TextUtils.formatSimple( + Log.d(TAG, formatSimple( "clearing cache %s of %d entries because nonce changed [%s] -> [%s]", cacheName(), mCache.size(), mLastSeenNonce, currentNonce)); @@ -824,7 +1044,7 @@ public class PropertyInvalidatedCache<Query, Result> { if (currentNonce != afterRefreshNonce) { currentNonce = afterRefreshNonce; if (DEBUG) { - Log.d(TAG, TextUtils.formatSimple( + Log.d(TAG, formatSimple( "restarting %s %s because nonce changed in refresh", cacheName(), queryToString(query))); @@ -895,10 +1115,7 @@ public class PropertyInvalidatedCache<Query, Result> { * @param name Name of the cache-key property to invalidate */ private static void disableSystemWide(@NonNull String name) { - if (!sEnabled) { - return; - } - setNonce(name, NONCE_DISABLED); + getNonceHandler(name).disable(); } /** @@ -908,7 +1125,7 @@ public class PropertyInvalidatedCache<Query, Result> { */ @TestApi public void invalidateCache() { - invalidateCache(mPropertyName); + mNonce.invalidate(); } /** @@ -931,59 +1148,7 @@ public class PropertyInvalidatedCache<Query, Result> { * @hide */ public static void invalidateCache(@NonNull String name) { - if (!sEnabled) { - if (DEBUG) { - Log.w(TAG, TextUtils.formatSimple( - "cache invalidate %s suppressed", name)); - } - return; - } - - // Take the cork lock so invalidateCache() racing against corkInvalidations() doesn't - // clobber a cork-written NONCE_UNSET with a cache key we compute before the cork. - // The property service is single-threaded anyway, so we don't lose any concurrency by - // taking the cork lock around cache invalidations. If we see contention on this lock, - // we're invalidating too often. - synchronized (sCorkLock) { - Integer numberCorks = sCorks.get(name); - if (numberCorks != null && numberCorks > 0) { - if (DEBUG) { - Log.d(TAG, "ignoring invalidation due to cork: " + name); - } - final long count = sCorkedInvalidates.getOrDefault(name, (long) 0); - sCorkedInvalidates.put(name, count + 1); - return; - } - invalidateCacheLocked(name); - } - } - - @GuardedBy("sCorkLock") - private static void invalidateCacheLocked(@NonNull String name) { - // There's no race here: we don't require that values strictly increase, but instead - // only that each is unique in a single runtime-restart session. - final long nonce = getNonce(name); - if (nonce == NONCE_DISABLED) { - if (DEBUG) { - Log.d(TAG, "refusing to invalidate disabled cache: " + name); - } - return; - } - - long newValue; - do { - newValue = NoPreloadHolder.next(); - } while (isReservedNonce(newValue)); - if (DEBUG) { - Log.d(TAG, TextUtils.formatSimple( - "invalidating cache [%s]: [%s] -> [%s]", - name, nonce, Long.toString(newValue))); - } - // There is a small race with concurrent disables here. A compare-and-exchange - // property operation would be required to eliminate the race condition. - setNonce(name, newValue); - long invalidateCount = sInvalidates.getOrDefault(name, (long) 0); - sInvalidates.put(name, ++invalidateCount); + getNonceHandler(name).invalidate(); } /** @@ -1000,43 +1165,7 @@ public class PropertyInvalidatedCache<Query, Result> { * @hide */ public static void corkInvalidations(@NonNull String name) { - if (!sEnabled) { - if (DEBUG) { - Log.w(TAG, TextUtils.formatSimple( - "cache cork %s suppressed", name)); - } - return; - } - - synchronized (sCorkLock) { - int numberCorks = sCorks.getOrDefault(name, 0); - if (DEBUG) { - Log.d(TAG, TextUtils.formatSimple( - "corking %s: numberCorks=%s", name, numberCorks)); - } - - // If we're the first ones to cork this cache, set the cache to the corked state so - // existing caches talk directly to their services while we've corked updates. - // Make sure we don't clobber a disabled cache value. - - // TODO(dancol): we can skip this property write and leave the cache enabled if the - // caller promises not to make observable changes to the cache backing state before - // uncorking the cache, e.g., by holding a read lock across the cork-uncork pair. - // Implement this more dangerous mode of operation if necessary. - if (numberCorks == 0) { - final long nonce = getNonce(name); - if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) { - setNonce(name, NONCE_CORKED); - } - } else { - final long count = sCorkedInvalidates.getOrDefault(name, (long) 0); - sCorkedInvalidates.put(name, count + 1); - } - sCorks.put(name, numberCorks + 1); - if (DEBUG) { - Log.d(TAG, "corked: " + name); - } - } + getNonceHandler(name).cork(); } /** @@ -1048,34 +1177,7 @@ public class PropertyInvalidatedCache<Query, Result> { * @hide */ public static void uncorkInvalidations(@NonNull String name) { - if (!sEnabled) { - if (DEBUG) { - Log.w(TAG, TextUtils.formatSimple( - "cache uncork %s suppressed", name)); - } - return; - } - - synchronized (sCorkLock) { - int numberCorks = sCorks.getOrDefault(name, 0); - if (DEBUG) { - Log.d(TAG, TextUtils.formatSimple( - "uncorking %s: numberCorks=%s", name, numberCorks)); - } - - if (numberCorks < 1) { - throw new AssertionError("cork underflow: " + name); - } - if (numberCorks == 1) { - sCorks.remove(name); - invalidateCacheLocked(name); - if (DEBUG) { - Log.d(TAG, "uncorked: " + name); - } - } else { - sCorks.put(name, numberCorks - 1); - } - } + getNonceHandler(name).uncork(); } /** @@ -1104,6 +1206,8 @@ public class PropertyInvalidatedCache<Query, Result> { @GuardedBy("mLock") private Handler mHandler; + private NonceHandler mNonce; + public AutoCorker(@NonNull String propertyName) { this(propertyName, DEFAULT_AUTO_CORK_DELAY_MS); } @@ -1117,31 +1221,35 @@ public class PropertyInvalidatedCache<Query, Result> { } public void autoCork() { + synchronized (mLock) { + if (mNonce == null) { + mNonce = getNonceHandler(mPropertyName); + } + } + if (getLooper() == null) { // We're not ready to auto-cork yet, so just invalidate the cache immediately. if (DEBUG) { Log.w(TAG, "invalidating instead of autocorking early in init: " + mPropertyName); } - PropertyInvalidatedCache.invalidateCache(mPropertyName); + mNonce.invalidate(); return; } synchronized (mLock) { boolean alreadyQueued = mUncorkDeadlineMs >= 0; if (DEBUG) { - Log.w(TAG, TextUtils.formatSimple( + Log.d(TAG, formatSimple( "autoCork %s mUncorkDeadlineMs=%s", mPropertyName, mUncorkDeadlineMs)); } mUncorkDeadlineMs = SystemClock.uptimeMillis() + mAutoCorkDelayMs; if (!alreadyQueued) { getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs); - PropertyInvalidatedCache.corkInvalidations(mPropertyName); + mNonce.cork(); } else { - synchronized (sCorkLock) { - final long count = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0); - sCorkedInvalidates.put(mPropertyName, count + 1); - } + // Count this as a corked invalidation. + mNonce.invalidate(); } } } @@ -1149,7 +1257,7 @@ public class PropertyInvalidatedCache<Query, Result> { private void handleMessage(Message msg) { synchronized (mLock) { if (DEBUG) { - Log.w(TAG, TextUtils.formatSimple( + Log.d(TAG, formatSimple( "handleMsesage %s mUncorkDeadlineMs=%s", mPropertyName, mUncorkDeadlineMs)); } @@ -1161,7 +1269,7 @@ public class PropertyInvalidatedCache<Query, Result> { if (mUncorkDeadlineMs > nowMs) { mUncorkDeadlineMs = nowMs + mAutoCorkDelayMs; if (DEBUG) { - Log.w(TAG, TextUtils.formatSimple( + Log.d(TAG, formatSimple( "scheduling uncork at %s", mUncorkDeadlineMs)); } @@ -1169,10 +1277,10 @@ public class PropertyInvalidatedCache<Query, Result> { return; } if (DEBUG) { - Log.w(TAG, "automatic uncorking " + mPropertyName); + Log.d(TAG, "automatic uncorking " + mPropertyName); } mUncorkDeadlineMs = -1; - PropertyInvalidatedCache.uncorkInvalidations(mPropertyName); + mNonce.uncork(); } } @@ -1207,7 +1315,7 @@ public class PropertyInvalidatedCache<Query, Result> { Result resultToCompare = recompute(query); boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce); if (!nonceChanged && !resultEquals(proposedResult, resultToCompare)) { - Log.e(TAG, TextUtils.formatSimple( + Log.e(TAG, formatSimple( "cache %s inconsistent for %s is %s should be %s", cacheName(), queryToString(query), proposedResult, resultToCompare)); @@ -1284,17 +1392,9 @@ public class PropertyInvalidatedCache<Query, Result> { /** * Returns a list of caches alive at the current time. */ - @GuardedBy("sGlobalLock") private static @NonNull ArrayList<PropertyInvalidatedCache> getActiveCaches() { - return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet()); - } - - /** - * Returns a list of the active corks in a process. - */ - private static @NonNull ArrayList<Map.Entry<String, Integer>> getActiveCorks() { - synchronized (sCorkLock) { - return new ArrayList<Map.Entry<String, Integer>>(sCorks.entrySet()); + synchronized (sGlobalLock) { + return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet()); } } @@ -1361,32 +1461,27 @@ public class PropertyInvalidatedCache<Query, Result> { return; } - long invalidateCount; - long corkedInvalidates; - synchronized (sCorkLock) { - invalidateCount = sInvalidates.getOrDefault(mPropertyName, (long) 0); - corkedInvalidates = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0); - } + NonceHandler.Stats stats = mNonce.getStats(); synchronized (mLock) { - pw.println(TextUtils.formatSimple(" Cache Name: %s", cacheName())); - pw.println(TextUtils.formatSimple(" Property: %s", mPropertyName)); + pw.println(formatSimple(" Cache Name: %s", cacheName())); + pw.println(formatSimple(" Property: %s", mPropertyName)); final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED] + mSkips[NONCE_BYPASS]; - pw.println(TextUtils.formatSimple( + pw.println(formatSimple( " Hits: %d, Misses: %d, Skips: %d, Clears: %d", mHits, mMisses, skips, mClears)); - pw.println(TextUtils.formatSimple( + pw.println(formatSimple( " Skip-corked: %d, Skip-unset: %d, Skip-bypass: %d, Skip-other: %d", mSkips[NONCE_CORKED], mSkips[NONCE_UNSET], mSkips[NONCE_BYPASS], mSkips[NONCE_DISABLED])); - pw.println(TextUtils.formatSimple( + pw.println(formatSimple( " Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d", - mLastSeenNonce, invalidateCount, corkedInvalidates)); - pw.println(TextUtils.formatSimple( + mLastSeenNonce, stats.invalidated, stats.corkedInvalidates)); + pw.println(formatSimple( " Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d", mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow)); - pw.println(TextUtils.formatSimple(" Enabled: %s", mDisabled ? "false" : "true")); + pw.println(formatSimple(" Enabled: %s", mDisabled ? "false" : "true")); pw.println(""); // No specific cache was requested. This is the default, and no details @@ -1404,23 +1499,7 @@ public class PropertyInvalidatedCache<Query, Result> { String key = Objects.toString(entry.getKey()); String value = Objects.toString(entry.getValue()); - pw.println(TextUtils.formatSimple(" Key: %s\n Value: %s\n", key, value)); - } - } - } - - /** - * Dump the corking status. - */ - @GuardedBy("sCorkLock") - private static void dumpCorkInfo(PrintWriter pw) { - ArrayList<Map.Entry<String, Integer>> activeCorks = getActiveCorks(); - if (activeCorks.size() > 0) { - pw.println(" Corking Status:"); - for (int i = 0; i < activeCorks.size(); i++) { - Map.Entry<String, Integer> entry = activeCorks.get(i); - pw.println(TextUtils.formatSimple(" Property Name: %s Count: %d", - entry.getKey(), entry.getValue())); + pw.println(formatSimple(" Key: %s\n Value: %s\n", key, value)); } } } @@ -1441,14 +1520,7 @@ public class PropertyInvalidatedCache<Query, Result> { // then only that cache is reported. boolean detail = anyDetailed(args); - ArrayList<PropertyInvalidatedCache> activeCaches; - synchronized (sGlobalLock) { - activeCaches = getActiveCaches(); - if (!detail) { - dumpCorkInfo(pw); - } - } - + ArrayList<PropertyInvalidatedCache> activeCaches = getActiveCaches(); for (int i = 0; i < activeCaches.size(); i++) { PropertyInvalidatedCache currentCache = activeCaches.get(i); currentCache.dumpContents(pw, detail, args); diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index ea4148c8ffa1..cad96e348f4b 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -235,6 +235,8 @@ import android.safetycenter.SafetyCenterFrameworkInitializer; import android.scheduling.SchedulingFrameworkInitializer; import android.security.FileIntegrityManager; import android.security.IFileIntegrityService; +import android.security.advancedprotection.AdvancedProtectionManager; +import android.security.advancedprotection.IAdvancedProtectionService; import android.security.attestationverification.AttestationVerificationManager; import android.security.attestationverification.IAttestationVerificationManagerService; import android.service.oemlock.IOemLockService; @@ -1771,6 +1773,21 @@ public final class SystemServiceRegistry { return new SupervisionManager(ctx, service); } }); + if (android.security.Flags.aapmApi()) { + registerService(Context.ADVANCED_PROTECTION_SERVICE, AdvancedProtectionManager.class, + new CachedServiceFetcher<>() { + @Override + public AdvancedProtectionManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder iBinder = ServiceManager.getServiceOrThrow( + Context.ADVANCED_PROTECTION_SERVICE); + IAdvancedProtectionService service = + IAdvancedProtectionService.Stub.asInterface(iBinder); + return new AdvancedProtectionManager(service); + } + }); + } + // DO NOT do a flag check like this unless the flag is read-only. // (because this code is executed during preload in zygote.) // If the flag is mutable, the check should be inside CachedServiceFetcher. diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java index 439d988e2588..dca433696fe7 100644 --- a/core/java/android/app/appfunctions/AppFunctionManager.java +++ b/core/java/android/app/appfunctions/AppFunctionManager.java @@ -110,40 +110,6 @@ public final class AppFunctionManager { * * @param request the request to execute the app function * @param executor the executor to run the callback - * @param callback the callback to receive the function execution result. if the calling app - * does not own the app function or does not have {@code - * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code - * android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code - * ExecuteAppFunctionResponse.RESULT_DENIED}. - * @deprecated Use {@link #executeAppFunction(ExecuteAppFunctionRequest, Executor, - * CancellationSignal, Consumer)} instead. This method will be removed once usage references - * are updated. - */ - @RequiresPermission( - anyOf = { - Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, - Manifest.permission.EXECUTE_APP_FUNCTIONS - }, - conditional = true) - @UserHandleAware - @Deprecated - public void executeAppFunction( - @NonNull ExecuteAppFunctionRequest request, - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer<ExecuteAppFunctionResponse> callback) { - executeAppFunction(request, executor, new CancellationSignal(), callback); - } - - /** - * Executes the app function. - * - * <p>Note: Applications can execute functions they define. To execute functions defined in - * another component, apps would need to have {@code - * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code - * android.permission.EXECUTE_APP_FUNCTIONS}. - * - * @param request the request to execute the app function - * @param executor the executor to run the callback * @param cancellationSignal the cancellation signal to cancel the execution. * @param callback the callback to receive the function execution result. if the calling app * does not own the app function or does not have {@code diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java index 08ecced234a9..06d95f5270c3 100644 --- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java +++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java @@ -137,8 +137,10 @@ public class AppFunctionRuntimeMetadata extends GenericDocument { .TOKENIZER_TYPE_VERBATIM) .build()) .addProperty( - new AppSearchSchema.BooleanPropertyConfig.Builder(PROPERTY_ENABLED) + new AppSearchSchema.LongPropertyConfig.Builder(PROPERTY_ENABLED) .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE) .build()) .addProperty( new AppSearchSchema.StringPropertyConfig.Builder( @@ -212,19 +214,14 @@ public class AppFunctionRuntimeMetadata extends GenericDocument { } /** - * Sets an indicator specifying if the function is enabled or not. This would override the - * default enabled state in the static metadata ({@link - * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT}). Sets this to null - * to clear the override. - * TODO(369683073) Replace the tristate Boolean with IntDef EnabledState. + * Sets an indicator specifying the function enabled state. */ @NonNull public Builder setEnabled(@EnabledState int enabledState) { if (enabledState != APP_FUNCTION_STATE_DEFAULT && enabledState != APP_FUNCTION_STATE_ENABLED && enabledState != APP_FUNCTION_STATE_DISABLED) { - throw new IllegalArgumentException( - "Value of EnabledState is unsupported."); + throw new IllegalArgumentException("Value of EnabledState is unsupported."); } setPropertyLong(PROPERTY_ENABLED, enabledState); return this; diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java index ceca850a1037..63d187aa11ef 100644 --- a/core/java/android/app/appfunctions/AppFunctionService.java +++ b/core/java/android/app/appfunctions/AppFunctionService.java @@ -158,74 +158,6 @@ public abstract class AppFunctionService extends Service { * thread and dispatch the result with the given callback. You should always report back the * result using the callback, no matter if the execution was successful or not. * - * @param request The function execution request. - * @param callback A callback to report back the result. - * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, CancellationSignal, - * Consumer)} instead. This method will be removed once usage references are updated. - */ - @MainThread - @Deprecated - public void onExecuteFunction( - @NonNull ExecuteAppFunctionRequest request, - @NonNull Consumer<ExecuteAppFunctionResponse> callback) { - Log.w( - "AppFunctionService", - "Calling deprecated default implementation of onExecuteFunction"); - } - - /** - * Called by the system to execute a specific app function. - * - * <p>This method is triggered when the system requests your AppFunctionService to handle a - * particular function you have registered and made available. - * - * <p>To ensure proper routing of function requests, assign a unique identifier to each - * function. This identifier doesn't need to be globally unique, but it must be unique within - * your app. For example, a function to order food could be identified as "orderFood". In most - * cases this identifier should come from the ID automatically generated by the AppFunctions - * SDK. You can determine the specific function to invoke by calling {@link - * ExecuteAppFunctionRequest#getFunctionIdentifier()}. - * - * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker - * thread and dispatch the result with the given callback. You should always report back the - * result using the callback, no matter if the execution was successful or not. - * - * <p>This method also accepts a {@link CancellationSignal} that the app should listen to cancel - * the execution of function if requested by the system. - * - * @param request The function execution request. - * @param cancellationSignal A signal to cancel the execution. - * @param callback A callback to report back the result. - * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, String, - * CancellationSignal, Consumer)} instead. This method will be removed once usage references - * are updated. - */ - @MainThread - @Deprecated - public void onExecuteFunction( - @NonNull ExecuteAppFunctionRequest request, - @NonNull CancellationSignal cancellationSignal, - @NonNull Consumer<ExecuteAppFunctionResponse> callback) { - onExecuteFunction(request, callback); - } - - /** - * Called by the system to execute a specific app function. - * - * <p>This method is triggered when the system requests your AppFunctionService to handle a - * particular function you have registered and made available. - * - * <p>To ensure proper routing of function requests, assign a unique identifier to each - * function. This identifier doesn't need to be globally unique, but it must be unique within - * your app. For example, a function to order food could be identified as "orderFood". In most - * cases this identifier should come from the ID automatically generated by the AppFunctions - * SDK. You can determine the specific function to invoke by calling {@link - * ExecuteAppFunctionRequest#getFunctionIdentifier()}. - * - * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker - * thread and dispatch the result with the given callback. You should always report back the - * result using the callback, no matter if the execution was successful or not. - * * <p>This method also accepts a {@link CancellationSignal} that the app should listen to cancel * the execution of function if requested by the system. * @@ -235,11 +167,9 @@ public abstract class AppFunctionService extends Service { * @param callback A callback to report back the result. */ @MainThread - public void onExecuteFunction( + public abstract void onExecuteFunction( @NonNull ExecuteAppFunctionRequest request, @NonNull String callingPackage, @NonNull CancellationSignal cancellationSignal, - @NonNull Consumer<ExecuteAppFunctionResponse> callback) { - onExecuteFunction(request, cancellationSignal, callback); - } + @NonNull Consumer<ExecuteAppFunctionResponse> callback); } diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 11e885055162..37fa9a26b91c 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -6,6 +6,14 @@ container: "system" # flag relates to. flag { + name: "notifications_redesign_app_icons" + namespace: "systemui" + description: "Notifications Redesign: Use app icons in notification rows (not to be confused with" + " notifications_use_app_icons, notifications_use_app_icon_in_row which are just experiments)." + bug: "371174789" +} + +flag { name: "modes_api" is_exported: true namespace: "systemui" diff --git a/core/java/android/app/usage/UsageEventsQuery.java b/core/java/android/app/usage/UsageEventsQuery.java index c0f13ca557e2..ecf4cd115fef 100644 --- a/core/java/android/app/usage/UsageEventsQuery.java +++ b/core/java/android/app/usage/UsageEventsQuery.java @@ -75,7 +75,7 @@ public final class UsageEventsQuery implements Parcelable { } /** - * Returns the exclusive timpstamp to indicate the end of the range of events. + * Returns the exclusive timestamp to indicate the end of the range of events. * Defined in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}. */ public @CurrentTimeMillisLong long getEndTimeMillis() { diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java index 7f30d7cccb57..124973489dd1 100644 --- a/core/java/android/companion/AssociationInfo.java +++ b/core/java/android/companion/AssociationInfo.java @@ -287,7 +287,7 @@ public final class AssociationInfo implements Parcelable { /** * Get the device icon of the associated device. The device icon represents the device type. * - * @return the device icon, or {@code null} if no device icon is has been set for the + * @return the device icon, or {@code null} if no device icon has been set for the * associated device. * * @see AssociationRequest.Builder#setDeviceIcon(Icon) diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 41a6791d8a7b..f368935a74c8 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -475,8 +475,8 @@ public final class AssociationRequest implements Parcelable { } /** - * Set the device icon for the self-managed device and this icon will be - * displayed in the self-managed association dialog. + * Set the device icon for the self-managed device and to display the icon in the + * self-managed association dialog. * * @throws IllegalArgumentException if the icon is not exactly 24dp by 24dp * or if it is {@link Icon#TYPE_URI} or {@link Icon#TYPE_URI_ADAPTIVE_BITMAP}. diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index dfad6de4ba16..4472c3d13d7c 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -478,6 +478,15 @@ public final class CompanionDeviceManager { Objects.requireNonNull(callback, "Callback cannot be null"); handler = Handler.mainIfNull(handler); + if (Flags.associationDeviceIcon()) { + final Icon deviceIcon = request.getDeviceIcon(); + + if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) { + throw new IllegalArgumentException("The size of the device icon must be " + + "24dp x 24dp to ensure proper display"); + } + } + try { mService.associate(request, new AssociationRequestCallbackProxy(handler, callback), mContext.getOpPackageName(), mContext.getUserId()); @@ -542,11 +551,13 @@ public final class CompanionDeviceManager { Objects.requireNonNull(executor, "Executor cannot be null"); Objects.requireNonNull(callback, "Callback cannot be null"); - final Icon deviceIcon = request.getDeviceIcon(); + if (Flags.associationDeviceIcon()) { + final Icon deviceIcon = request.getDeviceIcon(); - if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) { - throw new IllegalArgumentException("The size of the device icon must be 24dp x 24dp to" - + "ensure proper display"); + if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) { + throw new IllegalArgumentException("The size of the device icon must be " + + "24dp x 24dp to ensure proper display"); + } } try { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 91f7a8bae163..ffa33757599b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4325,6 +4325,7 @@ public abstract class Context { //@hide: ECM_ENHANCED_CONFIRMATION_SERVICE, CONTACT_KEYS_SERVICE, RANGING_SERVICE, + ADVANCED_PROTECTION_SERVICE, }) @Retention(RetentionPolicy.SOURCE) @@ -5658,6 +5659,14 @@ public abstract class Context { public static final String BINARY_TRANSPARENCY_SERVICE = "transparency"; /** + * System service name for ForensicService. + * The service manages the forensic info on device. + * @hide + */ + @FlaggedApi(android.security.Flags.FLAG_AFL_API) + public static final String FORENSIC_SERVICE = "forensic"; + + /** * System service name for the DeviceIdleManager. * @see #getSystemService(String) * @hide @@ -6368,6 +6377,15 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.security.advancedprotection.AdvancedProtectionManager} + * @see #getSystemService(String) + * @see android.security.advancedprotection.AdvancedProtectionManager + */ + @FlaggedApi(android.security.Flags.FLAG_AAPM_API) + public static final String ADVANCED_PROTECTION_SERVICE = "advanced_protection"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an * {@link android.security.FileIntegrityManager}. * @see #getSystemService(String) * @see android.security.FileIntegrityManager diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 0bb0027fb0c3..f71952849872 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -888,6 +888,22 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_ACTIVITY_RECOGNIZER = "android.intent.action.ACTIVITY_RECOGNIZER"; + /** @hide */ + public static void maybeMarkAsMissingCreatorToken(Object object) { + if (object instanceof Intent intent) { + maybeMarkAsMissingCreatorTokenInternal(intent); + } + } + + private static void maybeMarkAsMissingCreatorTokenInternal(Intent intent) { + boolean isForeign = (intent.mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0; + boolean isWithoutTrustedCreatorToken = + (intent.mLocalFlags & Intent.LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT) == 0; + if (isForeign && isWithoutTrustedCreatorToken) { + intent.addExtendedFlags(EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN); + } + } + /** * Represents a shortcut/live folder icon resource. * @@ -7684,10 +7700,8 @@ public class Intent implements Parcelable, Cloneable { /** * This flag indicates the creator token of this intent has been verified. - * - * @hide */ - public static final int LOCAL_FLAG_CREATOR_TOKEN_VERIFIED = 1 << 6; + private static final int LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT = 1 << 6; /** @hide */ @IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = { @@ -12243,6 +12257,30 @@ public class Intent implements Parcelable, Cloneable { } } + /** @hide */ + public void checkCreatorToken() { + if (mExtras == null) return; + if (mCreatorTokenInfo != null && mCreatorTokenInfo.mExtraIntentKeys != null) { + for (String key : mCreatorTokenInfo.mExtraIntentKeys) { + try { + Intent extraIntent = mExtras.getParcelable(key, Intent.class); + if (extraIntent == null) { + Log.w(TAG, "The key {" + key + + "} does not correspond to an intent in the bundle."); + continue; + } + extraIntent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT; + } catch (Exception e) { + Log.e(TAG, "Failed to validate creator token. key: " + key + ".", e); + } + } + } + // mark the bundle as intent extras after calls to getParcelable. + // otherwise, the logic to mark missing token would run before + // mark trusted creator token present. + mExtras.setIsIntentExtra(); + } + public void writeToParcel(Parcel out, int flags) { out.writeString8(mAction); Uri.writeToParcel(out, mData); @@ -12730,6 +12768,7 @@ public class Intent implements Parcelable, Cloneable { } mLocalFlags |= localFlags; + checkCreatorToken(); // Special attribution fix-up logic for any BluetoothDevice extras // passed via Bluetooth intents diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index 451c0e5e079a..c911326ccffd 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -93,4 +93,10 @@ interface IPackageInstaller { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})") void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") + int getVerificationPolicy(); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") + boolean setVerificationPolicy(int policy); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index c673d5846d5d..5da1444ac1db 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -62,6 +62,8 @@ import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.content.pm.verify.domain.DomainSet; +import android.content.pm.verify.pkg.VerificationSession; +import android.content.pm.verify.pkg.VerificationStatus; import android.graphics.Bitmap; import android.icu.util.ULocale; import android.net.Uri; @@ -418,6 +420,21 @@ public class PackageInstaller { public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS"; /** + * When verification is blocked as part of the installation, additional reason for the block + * will be provided to the installer with a {@link VerificationFailedReason} as part of the + * installation result returned via the {@link IntentSender} in + * {@link Session#commit(IntentSender)}. This extra is provided only when the installation has + * failed. Installers can use this extra to check if the installation failure was caused by a + * verification failure. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) + @SystemApi + public static final String EXTRA_VERIFICATION_FAILURE_REASON = + "android.content.pm.extra.VERIFICATION_FAILURE_REASON"; + + /** * Streaming installation pending. * Caller should make sure DataLoader is able to prepare image and reinitiate the operation. * @@ -760,6 +777,90 @@ public class PackageInstaller { @Retention(RetentionPolicy.SOURCE) public @interface UnarchivalStatus {} + /** + * Verification failed because of unknown reasons, such as when the verifier times out or cannot + * be connected. It can also corresponds to the status of + * {@link VerificationSession#VERIFICATION_INCOMPLETE_UNKNOWN} reported by the verifier via + * {@link VerificationSession#reportVerificationIncomplete(int)}. + * @hide + */ + @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) + @SystemApi + public static final int VERIFICATION_FAILED_REASON_UNKNOWN = 0; + + /** + * Verification failed because the network is unavailable. This corresponds to the status of + * {@link VerificationSession#VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE} reported by the + * verifier via {@link VerificationSession#reportVerificationIncomplete(int)}. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) + @SystemApi + public static final int VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE = 1; + + /** + * Verification failed because the package is blocked, as reported by the verifier via + * {@link VerificationSession#reportVerificationComplete(VerificationStatus)} or + * {@link VerificationSession#reportVerificationComplete(VerificationStatus, PersistableBundle)} + * @hide + */ + @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) + @SystemApi + public static final int VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED = 2; + + /** + * @hide + */ + @IntDef(value = { + VERIFICATION_FAILED_REASON_UNKNOWN, + VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE, + VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED, + }) + public @interface VerificationFailedReason { + } + + /** + * Do not block installs, regardless of verification status. + * @hide + */ + @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) + @SystemApi + public static final int VERIFICATION_POLICY_NONE = 0; // platform default + /** + * Only block installations on {@link #VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED}. + * @hide + */ + @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) + @SystemApi + public static final int VERIFICATION_POLICY_BLOCK_FAIL_OPEN = 1; + /** + * Only block installations on {@link #VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED} and ask the + * user if they'd like to install anyway when the verification is blocked for other reason. + * @hide + */ + @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) + @SystemApi + public static final int VERIFICATION_POLICY_BLOCK_FAIL_WARN = 2; + /** + * Block installations whose verification status is blocked for any reason. + * @hide + */ + @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) + @SystemApi + public static final int VERIFICATION_POLICY_BLOCK_FAIL_CLOSED = 3; + /** + * @hide + */ + @IntDef(value = { + VERIFICATION_POLICY_NONE, + VERIFICATION_POLICY_BLOCK_FAIL_OPEN, + VERIFICATION_POLICY_BLOCK_FAIL_WARN, + VERIFICATION_POLICY_BLOCK_FAIL_CLOSED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface VerificationPolicy { + } /** Default set of checksums - includes all available checksums. * @see Session#requestChecksums */ @@ -1503,6 +1604,40 @@ public class PackageInstaller { } /** + * Return the current verification enforcement policy. This may only be called by the + * package currently set by the system as the verifier agent. + * @hide + */ + @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) + @SystemApi + @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) + public final @VerificationPolicy int getVerificationPolicy() { + try { + return mInstaller.getVerificationPolicy(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set the current verification enforcement policy which will be applied to all the future + * installation sessions. This may only be called by the package currently set by the system as + * the verifier agent. + * @hide + * @return whether the new policy was successfully set. + */ + @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) + @SystemApi + @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) + public final boolean setVerificationPolicy(@VerificationPolicy int policy) { + try { + return mInstaller.setVerificationPolicy(policy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * An installation that is being actively staged. For an install to succeed, * all existing and new packages must have identical package names, version * codes, and signing certificates. @@ -2799,6 +2934,8 @@ public class PackageInstaller { public int unarchiveId = -1; /** {@hide} */ public @Nullable String dexoptCompilerFilter = null; + /** {@hide} */ + public boolean forceVerification; private final ArrayMap<String, Integer> mPermissionStates; @@ -2853,6 +2990,7 @@ public class PackageInstaller { developmentInstallFlags = source.readInt(); unarchiveId = source.readInt(); dexoptCompilerFilter = source.readString(); + forceVerification = source.readBoolean(); } /** {@hide} */ @@ -2889,6 +3027,7 @@ public class PackageInstaller { ret.developmentInstallFlags = developmentInstallFlags; ret.unarchiveId = unarchiveId; ret.dexoptCompilerFilter = dexoptCompilerFilter; + ret.forceVerification = forceVerification; return ret; } @@ -3597,6 +3736,14 @@ public class PackageInstaller { return grantedPermissions.toArray(ArrayUtils.emptyArray(String.class)); } + /** + * Used by adb installations to force enable the verification for this install. + * {@hide} + */ + public void setForceVerification() { + this.forceVerification = true; + } + /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); @@ -3632,6 +3779,7 @@ public class PackageInstaller { pw.printHexPair("developmentInstallFlags", developmentInstallFlags); pw.printPair("unarchiveId", unarchiveId); pw.printPair("dexoptCompilerFilter", dexoptCompilerFilter); + pw.printPair("forceVerification", forceVerification); pw.println(); } @@ -3678,6 +3826,7 @@ public class PackageInstaller { dest.writeInt(developmentInstallFlags); dest.writeInt(unarchiveId); dest.writeString(dexoptCompilerFilter); + dest.writeBoolean(forceVerification); } public static final Parcelable.Creator<SessionParams> diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index c7d7dc1eb0de..5b38942d468d 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -326,3 +326,19 @@ flag { bug: "360129103" is_fixed_read_only: true } + +flag { + name: "include_feature_flags_in_package_cacher" + namespace: "package_manager_service" + description: "Include feature flag status when determining hits or misses in PackageCacher." + bug: "364771256" + is_fixed_read_only: true +} + +flag { + name: "reduce_broadcasts_for_component_state_changes" + namespace: "package_manager_service" + description: "Feature flag to limit sending of the PACKAGE_CHANGED broadcast to only the system and the application itself during component state changes." + bug: "292261144" + is_fixed_read_only: true +} diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 7de7131fc2ad..fa3bc9e16a6b 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -211,6 +211,18 @@ flag { } flag { + name: "property_invalidated_cache_bypass_mismatched_uids" + namespace: "multiuser" + description: "Bypass the cache when the process UID does not match the binder UID." + bug: "373752556" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} + + +flag { name: "cache_profile_parent_read_only" namespace: "multiuser" description: "Cache getProfileParent to avoid unnecessary binder calls" diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionCallback.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionCallback.aidl deleted file mode 100644 index 38a7956603ae..000000000000 --- a/core/java/android/content/pm/verify/pkg/IVerificationSessionCallback.aidl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.pm.verify.pkg; - -import android.content.pm.verify.pkg.VerificationStatus; -import android.os.PersistableBundle; - -/** - * Oneway interface that allows the verifier to send response or verification results back to - * the system. - * @hide - */ -oneway interface IVerificationSessionCallback { - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") - void reportVerificationIncomplete(int verificationId, int reason); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") - void reportVerificationComplete(int verificationId, in VerificationStatus status); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") - void reportVerificationCompleteWithExtensionResponse(int verificationId, in VerificationStatus status, in PersistableBundle response); -} diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl index 7a9484abd1b1..66caf2d0fec0 100644 --- a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl +++ b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl @@ -16,8 +16,11 @@ package android.content.pm.verify.pkg; +import android.content.pm.verify.pkg.VerificationStatus; +import android.os.PersistableBundle; + /** - * Non-oneway interface that allows the verifier to retrieve information from the system. + * Non-oneway interface that allows the verifier to communicate with the system. * @hide */ interface IVerificationSessionInterface { @@ -25,4 +28,12 @@ interface IVerificationSessionInterface { long getTimeoutTime(int verificationId); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") long extendTimeRemaining(int verificationId, long additionalMs); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") + boolean setVerificationPolicy(int verificationId, int policy); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") + void reportVerificationIncomplete(int verificationId, int reason); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") + void reportVerificationComplete(int verificationId, in VerificationStatus status); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") + void reportVerificationCompleteWithExtensionResponse(int verificationId, in VerificationStatus status, in PersistableBundle response); }
\ No newline at end of file diff --git a/core/java/android/content/pm/verify/pkg/VerificationSession.java b/core/java/android/content/pm/verify/pkg/VerificationSession.java index 70b4a022f521..4ade21198f37 100644 --- a/core/java/android/content/pm/verify/pkg/VerificationSession.java +++ b/core/java/android/content/pm/verify/pkg/VerificationSession.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.pm.Flags; +import android.content.pm.PackageInstaller; import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningInfo; import android.net.Uri; @@ -52,17 +53,12 @@ public final class VerificationSession implements Parcelable { * The verification cannot be completed because the network is unavailable. */ public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; - /** - * The verification cannot be completed because the network is limited. - */ - public static final int VERIFICATION_INCOMPLETE_NETWORK_LIMITED = 2; /** * @hide */ @IntDef(prefix = {"VERIFICATION_INCOMPLETE_"}, value = { VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE, - VERIFICATION_INCOMPLETE_NETWORK_LIMITED, VERIFICATION_INCOMPLETE_UNKNOWN, }) @Retention(RetentionPolicy.SOURCE) @@ -83,8 +79,15 @@ public final class VerificationSession implements Parcelable { private final PersistableBundle mExtensionParams; @NonNull private final IVerificationSessionInterface mSession; - @NonNull - private final IVerificationSessionCallback mCallback; + /** + * The current policy that is active for the session. It might not be + * the same as the original policy that was initially assigned for this verification session, + * because the active policy can be overridden by {@link #setVerificationPolicy(int)}. + * <p>To improve the latency, store the original policy value and any changes made to it, + * so that {@link #getVerificationPolicy()} does not need to make a binder call to retrieve the + * currently active policy.</p> + */ + private volatile @PackageInstaller.VerificationPolicy int mVerificationPolicy; /** * Constructor used by the system to describe the details of a verification session. @@ -94,8 +97,8 @@ public final class VerificationSession implements Parcelable { @NonNull Uri stagedPackageUri, @NonNull SigningInfo signingInfo, @NonNull List<SharedLibraryInfo> declaredLibraries, @NonNull PersistableBundle extensionParams, - @NonNull IVerificationSessionInterface session, - @NonNull IVerificationSessionCallback callback) { + @PackageInstaller.VerificationPolicy int defaultPolicy, + @NonNull IVerificationSessionInterface session) { mId = id; mInstallSessionId = installSessionId; mPackageName = packageName; @@ -103,8 +106,8 @@ public final class VerificationSession implements Parcelable { mSigningInfo = signingInfo; mDeclaredLibraries = declaredLibraries; mExtensionParams = extensionParams; + mVerificationPolicy = defaultPolicy; mSession = session; - mCallback = callback; } /** @@ -144,7 +147,7 @@ public final class VerificationSession implements Parcelable { /** * Returns a mapping of any shared libraries declared in the manifest - * to the {@link SharedLibraryInfo#Type} that is declared. This will be an empty + * to the {@link SharedLibraryInfo.Type} that is declared. This will be an empty * map if no shared libraries are declared by the package. */ @NonNull @@ -174,6 +177,39 @@ public final class VerificationSession implements Parcelable { } /** + * Return the current policy that is active for this session. + * <p>If the policy for this session has been changed by {@link #setVerificationPolicy}, + * the return value of this method is the current policy that is active for this session. + * Otherwise, the return value is the same as the initial policy that was assigned to the + * session when it was first created.</p> + */ + public @PackageInstaller.VerificationPolicy int getVerificationPolicy() { + return mVerificationPolicy; + } + + /** + * Override the verification policy for this session. + * @return True if the override was successful, False otherwise. + */ + @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) + public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) { + if (mVerificationPolicy == policy) { + // No effective policy change + return true; + } + try { + if (mSession.setVerificationPolicy(mId, policy)) { + mVerificationPolicy = policy; + return true; + } else { + return false; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Extend the timeout for this session by the provided additionalMs to * fetch relevant information over the network or wait for the network. * This may be called multiple times. If the request would bypass any max @@ -196,7 +232,7 @@ public final class VerificationSession implements Parcelable { @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(@VerificationIncompleteReason int reason) { try { - mCallback.reportVerificationIncomplete(mId, reason); + mSession.reportVerificationIncomplete(mId, reason); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -210,7 +246,7 @@ public final class VerificationSession implements Parcelable { @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull VerificationStatus status) { try { - mCallback.reportVerificationComplete(mId, status); + mSession.reportVerificationComplete(mId, status); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -225,7 +261,7 @@ public final class VerificationSession implements Parcelable { public void reportVerificationComplete(@NonNull VerificationStatus status, @NonNull PersistableBundle response) { try { - mCallback.reportVerificationCompleteWithExtensionResponse(mId, status, response); + mSession.reportVerificationCompleteWithExtensionResponse(mId, status, response); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -239,8 +275,8 @@ public final class VerificationSession implements Parcelable { mSigningInfo = SigningInfo.CREATOR.createFromParcel(in); mDeclaredLibraries = in.createTypedArrayList(SharedLibraryInfo.CREATOR); mExtensionParams = in.readPersistableBundle(getClass().getClassLoader()); + mVerificationPolicy = in.readInt(); mSession = IVerificationSessionInterface.Stub.asInterface(in.readStrongBinder()); - mCallback = IVerificationSessionCallback.Stub.asInterface(in.readStrongBinder()); } @Override @@ -257,8 +293,8 @@ public final class VerificationSession implements Parcelable { mSigningInfo.writeToParcel(dest, flags); dest.writeTypedList(mDeclaredLibraries); dest.writePersistableBundle(mExtensionParams); + dest.writeInt(mVerificationPolicy); dest.writeStrongBinder(mSession.asBinder()); - dest.writeStrongBinder(mCallback.asBinder()); } @NonNull diff --git a/core/java/android/content/pm/xr.aconfig b/core/java/android/content/pm/xr.aconfig new file mode 100644 index 000000000000..61835c162c49 --- /dev/null +++ b/core/java/android/content/pm/xr.aconfig @@ -0,0 +1,9 @@ +package: "android.xr" +container: "system" + +flag { + namespace: "xr" + name: "xr_manifest_entries" + description: "Adds manifest entries used by Android XR" + bug: "364416355" +}
\ No newline at end of file diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java index 8c87ad3353b6..93958443c01b 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -283,7 +283,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { private static NativeAllocationRegistry getRegistry(long size) { final long func = nGetNativeFinalizer(); final Class cls = HardwareBuffer.class; - return com.android.libcore.Flags.nativeMetrics() + return com.android.libcore.readonly.Flags.nativeMetrics() ? NativeAllocationRegistry.createNonmalloced(cls, func, size) : NativeAllocationRegistry.createNonmalloced(cls.getClassLoader(), func, size); } diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig index 99eeca98bce0..66697546a24a 100644 --- a/core/java/android/hardware/input/input_framework.aconfig +++ b/core/java/android/hardware/input/input_framework.aconfig @@ -27,14 +27,6 @@ flag { flag { namespace: "input_native" - name: "pointer_coords_is_resampled_api" - is_exported: true - description: "Makes MotionEvent.PointerCoords#isResampled() a public API" - bug: "298197511" -} - -flag { - namespace: "input_native" name: "emoji_and_screenshot_keycodes_available" is_exported: true description: "Add new KeyEvent keycodes for opening Emoji Picker and Taking Screenshots" @@ -142,3 +134,10 @@ flag { description: "Controls whether the connected mice's primary buttons, left and right, can be swapped." bug: "352598211" } + +flag { + name: "keyboard_a11y_shortcut_control" + namespace: "input" + description: "Adds shortcuts to toggle and control a11y features" + bug: "373458181" +} diff --git a/core/java/android/hardware/radio/RadioAlert.aidl b/core/java/android/hardware/radio/RadioAlert.aidl new file mode 100644 index 000000000000..17f4fc7e9a13 --- /dev/null +++ b/core/java/android/hardware/radio/RadioAlert.aidl @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.radio; + +/** @hide */ +parcelable RadioAlert;
\ No newline at end of file diff --git a/core/java/android/hardware/radio/RadioAlert.java b/core/java/android/hardware/radio/RadioAlert.java new file mode 100644 index 000000000000..9b93e73555f0 --- /dev/null +++ b/core/java/android/hardware/radio/RadioAlert.java @@ -0,0 +1,517 @@ +/** + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.radio; + +import android.annotation.FlaggedApi; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Emergency Alert Message + * + * <p>Alert message can be sent from a radio station of technologies such as HD radio to + * the radio users for some emergency events (see ITU-T X.1303 bis for more info). + * @hide + */ +@FlaggedApi(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) +public final class RadioAlert implements Parcelable { + + public static final class Geocode implements Parcelable { + + private final String mValueName; + private final String mValue; + + /** + * Constructor of geocode. + * + * @param valueName Name of geocode value + * @param value Value of geocode + * @hide + */ + public Geocode(@NonNull String valueName, @NonNull String value) { + mValueName = Objects.requireNonNull(valueName, "Geocode value name can not be null"); + mValue = Objects.requireNonNull(value, "Geocode value can not be null"); + } + + private Geocode(Parcel in) { + mValueName = in.readString8(); + mValue = in.readString8(); + } + + public static final @NonNull Creator<Geocode> CREATOR = new Creator<Geocode>() { + @Override + public Geocode createFromParcel(Parcel in) { + return new Geocode(in); + } + + @Override + public Geocode[] newArray(int size) { + return new Geocode[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mValueName); + dest.writeString8(mValue); + } + + @NonNull + @Override + public String toString() { + return "Gecode [valueName=" + mValueName + ", value=" + mValue + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mValueName, mValue); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Geocode other)) { + return false; + } + + return Objects.equals(mValueName, other.mValueName) + && Objects.equals(mValue, other.mValue); + } + } + + public static final class Coordinate implements Parcelable { + private final double mLatitude; + private final double mLongitude; + + /** + * Constructor of coordinate. + * + * @param latitude Latitude of the coordinate + * @param longitude Longitude of the coordinate + * @hide + */ + public Coordinate(double latitude, double longitude) { + if (latitude < -90.0 || latitude > 90.0) { + throw new IllegalArgumentException("Latitude value should be between -90 and 90"); + } + if (longitude < -180.0 || longitude > 180.0) { + throw new IllegalArgumentException( + "Longitude value should be between -180 and 180"); + } + mLatitude = latitude; + mLongitude = longitude; + } + + private Coordinate(Parcel in) { + mLatitude = in.readDouble(); + mLongitude = in.readDouble(); + } + + public static final @NonNull Creator<Coordinate> CREATOR = new Creator<Coordinate>() { + @Override + public Coordinate createFromParcel(Parcel in) { + return new Coordinate(in); + } + + @Override + public Coordinate[] newArray(int size) { + return new Coordinate[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeDouble(mLatitude); + dest.writeDouble(mLongitude); + } + + @NonNull + @Override + public String toString() { + return "Coordinate [latitude=" + mLatitude + ", longitude=" + mLongitude + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mLatitude, mLongitude); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Coordinate other)) { + return false; + } + return mLatitude == other.mLatitude && mLongitude == other.mLongitude; + } + } + + public static final class Polygon implements Parcelable { + + private final List<Coordinate> mCoordinates; + + /** + * Constructor of polygon. + * + * @param coordinates Coordinates the polygon is composed of + * @hide + */ + public Polygon(@NonNull List<Coordinate> coordinates) { + Objects.requireNonNull(coordinates, "Coordinates can not be null"); + if (coordinates.size() < 4) { + throw new IllegalArgumentException("Number of coordinates must be at least 4"); + } + if (!coordinates.get(0).equals(coordinates.get(coordinates.size() - 1))) { + throw new IllegalArgumentException( + "The last and first coordinates must be the same"); + } + mCoordinates = coordinates; + } + + private Polygon(Parcel in) { + mCoordinates = new ArrayList<>(); + in.readTypedList(mCoordinates, Coordinate.CREATOR); + } + + public static final @NonNull Creator<Polygon> CREATOR = new Creator<Polygon>() { + @Override + public Polygon createFromParcel(Parcel in) { + return new Polygon(in); + } + + @Override + public Polygon[] newArray(int size) { + return new Polygon[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mCoordinates); + } + + @NonNull + @Override + public String toString() { + return "Polygon [coordinates=" + mCoordinates + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mCoordinates); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Polygon other)) { + return false; + } + return mCoordinates.equals(other.mCoordinates); + } + } + + public static final class AlertArea implements Parcelable { + + private final List<Polygon> mPolygons; + private final List<Geocode> mGeocodes; + + /** + * Constructor of alert area. + * + * @param polygons Polygons used in alert area + * @param geocodes Geocodes used in alert area + * @hide + */ + public AlertArea(@NonNull List<Polygon> polygons, @NonNull List<Geocode> geocodes) { + mPolygons = Objects.requireNonNull(polygons, "Polygons can not be null"); + mGeocodes = Objects.requireNonNull(geocodes, "Geocodes can not be null"); + } + + private AlertArea(Parcel in) { + mPolygons = new ArrayList<>(); + mGeocodes = new ArrayList<>(); + in.readTypedList(mPolygons, Polygon.CREATOR); + in.readTypedList(mGeocodes, Geocode.CREATOR); + } + + public static final @NonNull Creator<AlertArea> CREATOR = new Creator<AlertArea>() { + @Override + public AlertArea createFromParcel(Parcel in) { + return new AlertArea(in); + } + + @Override + public AlertArea[] newArray(int size) { + return new AlertArea[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mPolygons); + dest.writeTypedList(mGeocodes); + } + + @NonNull + @Override + public String toString() { + return "AlertArea [polygons=" + mPolygons + ", geocodes=" + mGeocodes + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mPolygons, mGeocodes); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AlertArea other)) { + return false; + } + + return mPolygons.equals(other.mPolygons) && mGeocodes.equals(other.mGeocodes); + } + } + + public static final class AlertInfo implements Parcelable { + + private final List<Integer> mCategoryList; + private final int mUrgency; + private final int mSeverity; + private final int mCertainty; + private final String mTextualMessage; + private final List<AlertArea> mAreaList; + @Nullable private final String mLanguage; + + /** + * Constructor for alert info. + * + * @param categoryList Array of categories of the subject event of the alert message + * @param urgency The urgency of the subject event of the alert message + * @param severity The severity of the subject event of the alert message + * @param certainty The certainty of the subject event of the alert message + * @param textualMessage Textual descriptions of the subject event + * @param areaList The array of geographic areas to which the alert info segment in which + * it appears applies + * @param language The optional language field of the alert info + * @hide + */ + public AlertInfo(@NonNull List<Integer> categoryList, int urgency, + int severity, int certainty, String textualMessage, + @NonNull List<AlertArea> areaList, @Nullable String language) { + mCategoryList = Objects.requireNonNull(categoryList, "Category list can not be null"); + mUrgency = urgency; + mSeverity = severity; + mCertainty = certainty; + mTextualMessage = textualMessage; + mAreaList = Objects.requireNonNull(areaList, "Area list can not be null"); + mLanguage = language; + } + + private AlertInfo(Parcel in) { + mCategoryList = in.readArrayList(Integer.class.getClassLoader(), Integer.class); + mUrgency = in.readInt(); + mSeverity = in.readInt(); + mCertainty = in.readInt(); + mTextualMessage = in.readString8(); + mAreaList = new ArrayList<>(); + in.readTypedList(mAreaList, AlertArea.CREATOR); + boolean hasLanguage = in.readBoolean(); + if (hasLanguage) { + mLanguage = in.readString8(); + } else { + mLanguage = null; + } + } + + public static final @NonNull Creator<AlertInfo> CREATOR = new Creator<AlertInfo>() { + @Override + public AlertInfo createFromParcel(Parcel in) { + return new AlertInfo(in); + } + + @Override + public AlertInfo[] newArray(int size) { + return new AlertInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeList(mCategoryList); + dest.writeInt(mUrgency); + dest.writeInt(mSeverity); + dest.writeInt(mCertainty); + dest.writeString8(mTextualMessage); + dest.writeTypedList(mAreaList); + if (mLanguage == null) { + dest.writeBoolean(false); + } else { + dest.writeBoolean(true); + dest.writeString8(mLanguage); + } + } + + @NonNull + @Override + public String toString() { + return "AlertInfo [categoryList=" + mCategoryList + ", urgency=" + mUrgency + + ", severity=" + mSeverity + ", certainty=" + mCertainty + + ", textualMessage=" + mTextualMessage + ", areaList=" + mAreaList + + ", language=" + mLanguage + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mCategoryList, mUrgency, mSeverity, mCertainty, mTextualMessage, + mAreaList, mLanguage); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AlertInfo other)) { + return false; + } + + return mCategoryList.equals(other.mCategoryList) && mUrgency == other.mUrgency + && mSeverity == other.mSeverity && mCertainty == other.mCertainty + && mTextualMessage.equals(other.mTextualMessage) + && mAreaList.equals(other.mAreaList) + && Objects.equals(mLanguage, other.mLanguage); + } + } + + private final int mStatus; + private final int mMessageType; + private final List<AlertInfo> mInfoList; + + /** + * Constructor of radio alert message. + * + * @param status Status of alert message + * @param messageType Message type of alert message + * @param infoList List of alert info + * @hide + */ + public RadioAlert(int status, int messageType, + @NonNull List<AlertInfo> infoList) { + mStatus = status; + mMessageType = messageType; + mInfoList = Objects.requireNonNull(infoList, "Alert info list can not be null"); + } + + private RadioAlert(Parcel in) { + mStatus = in.readInt(); + mMessageType = in.readInt(); + mInfoList = in.readParcelableList(new ArrayList<>(), AlertInfo.class.getClassLoader(), + AlertInfo.class); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mStatus); + dest.writeInt(mMessageType); + dest.writeParcelableList(mInfoList, /* flags= */ 0); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + @Override + public String toString() { + return "RadioAlert [status=" + mStatus + ", messageType=" + mMessageType + + ", infoList= " + mInfoList + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mStatus, mMessageType, mInfoList); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof RadioAlert other)) { + return false; + } + + return mStatus == other.mStatus && mMessageType == other.mMessageType + && mInfoList.equals(other.mInfoList); + } + + public static final @NonNull Creator<RadioAlert> CREATOR = new Creator<RadioAlert>() { + @Override + public RadioAlert createFromParcel(Parcel in) { + return new RadioAlert(in); + } + + @Override + public RadioAlert[] newArray(int size) { + return new RadioAlert[size]; + } + }; +} diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index 49ab15a40a8e..50121278f0e6 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -21,6 +21,7 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Intent; import android.util.ArrayMap; import android.util.Log; import android.util.MathUtils; @@ -401,6 +402,9 @@ public class BaseBundle { synchronized (this) { object = unwrapLazyValueFromMapLocked(i, clazz, itemTypes); } + if ((mFlags & Bundle.FLAG_VERIFY_TOKENS_PRESENT) != 0) { + Intent.maybeMarkAsMissingCreatorToken(object); + } } return (clazz != null) ? clazz.cast(object) : (T) object; } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index d3ba73ed1421..a89483394611 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1278,6 +1278,41 @@ public class Build { /** @hide */ @IntDef(value = { + VERSION_CODES_FULL.BASE, + VERSION_CODES_FULL.BASE_1_1, + VERSION_CODES_FULL.CUPCAKE, + VERSION_CODES_FULL.DONUT, + VERSION_CODES_FULL.ECLAIR, + VERSION_CODES_FULL.ECLAIR_0_1, + VERSION_CODES_FULL.ECLAIR_MR1, + VERSION_CODES_FULL.FROYO, + VERSION_CODES_FULL.GINGERBREAD, + VERSION_CODES_FULL.GINGERBREAD_MR1, + VERSION_CODES_FULL.HONEYCOMB, + VERSION_CODES_FULL.HONEYCOMB_MR1, + VERSION_CODES_FULL.HONEYCOMB_MR2, + VERSION_CODES_FULL.ICE_CREAM_SANDWICH, + VERSION_CODES_FULL.ICE_CREAM_SANDWICH_MR1, + VERSION_CODES_FULL.JELLY_BEAN, + VERSION_CODES_FULL.JELLY_BEAN_MR1, + VERSION_CODES_FULL.JELLY_BEAN_MR2, + VERSION_CODES_FULL.KITKAT, + VERSION_CODES_FULL.KITKAT_WATCH, + VERSION_CODES_FULL.LOLLIPOP, + VERSION_CODES_FULL.LOLLIPOP_MR1, + VERSION_CODES_FULL.M, + VERSION_CODES_FULL.N, + VERSION_CODES_FULL.N_MR1, + VERSION_CODES_FULL.O, + VERSION_CODES_FULL.O_MR1, + VERSION_CODES_FULL.P, + VERSION_CODES_FULL.Q, + VERSION_CODES_FULL.R, + VERSION_CODES_FULL.S, + VERSION_CODES_FULL.S_V2, + VERSION_CODES_FULL.TIRAMISU, + VERSION_CODES_FULL.UPSIDE_DOWN_CAKE, + VERSION_CODES_FULL.VANILLA_ICE_CREAM, }) @Retention(RetentionPolicy.SOURCE) public @interface SdkIntFull {} @@ -1299,6 +1334,186 @@ public class Build { // Use the last 5 digits for the minor version. This allows the // minor version to be set to CUR_DEVELOPMENT. private static final int SDK_INT_MULTIPLIER = 100000; + + /** + * Android 1.0. + */ + public static final int BASE = VERSION_CODES.BASE * SDK_INT_MULTIPLIER; + + /** + * Android 2.0. + */ + public static final int BASE_1_1 = VERSION_CODES.BASE_1_1 * SDK_INT_MULTIPLIER; + + /** + * Android 3.0. + */ + public static final int CUPCAKE = VERSION_CODES.CUPCAKE * SDK_INT_MULTIPLIER; + + /** + * Android 4.0. + */ + public static final int DONUT = VERSION_CODES.DONUT * SDK_INT_MULTIPLIER; + + /** + * Android 5.0. + */ + public static final int ECLAIR = VERSION_CODES.ECLAIR * SDK_INT_MULTIPLIER; + + /** + * Android 6.0. + */ + public static final int ECLAIR_0_1 = VERSION_CODES.ECLAIR_0_1 * SDK_INT_MULTIPLIER; + + /** + * Android 7.0. + */ + public static final int ECLAIR_MR1 = VERSION_CODES.ECLAIR_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 8.0. + */ + public static final int FROYO = VERSION_CODES.FROYO * SDK_INT_MULTIPLIER; + + /** + * Android 9.0. + */ + public static final int GINGERBREAD = VERSION_CODES.GINGERBREAD * SDK_INT_MULTIPLIER; + + /** + * Android 10.0. + */ + public static final int GINGERBREAD_MR1 = + VERSION_CODES.GINGERBREAD_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 11.0. + */ + public static final int HONEYCOMB = VERSION_CODES.HONEYCOMB * SDK_INT_MULTIPLIER; + + /** + * Android 12.0. + */ + public static final int HONEYCOMB_MR1 = VERSION_CODES.HONEYCOMB_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 13.0. + */ + public static final int HONEYCOMB_MR2 = VERSION_CODES.HONEYCOMB_MR2 * SDK_INT_MULTIPLIER; + + /** + * Android 14.0. + */ + public static final int ICE_CREAM_SANDWICH = + VERSION_CODES.ICE_CREAM_SANDWICH * SDK_INT_MULTIPLIER; + + /** + * Android 15.0. + */ + public static final int ICE_CREAM_SANDWICH_MR1 = + VERSION_CODES.ICE_CREAM_SANDWICH_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 16.0. + */ + public static final int JELLY_BEAN = VERSION_CODES.JELLY_BEAN * SDK_INT_MULTIPLIER; + + /** + * Android 17.0. + */ + public static final int JELLY_BEAN_MR1 = VERSION_CODES.JELLY_BEAN_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 18.0. + */ + public static final int JELLY_BEAN_MR2 = VERSION_CODES.JELLY_BEAN_MR2 * SDK_INT_MULTIPLIER; + + /** + * Android 19.0. + */ + public static final int KITKAT = VERSION_CODES.KITKAT * SDK_INT_MULTIPLIER; + + /** + * Android 20.0. + */ + public static final int KITKAT_WATCH = VERSION_CODES.KITKAT_WATCH * SDK_INT_MULTIPLIER; + + /** + * Android 21.0. + */ + public static final int LOLLIPOP = VERSION_CODES.LOLLIPOP * SDK_INT_MULTIPLIER; + + /** + * Android 22.0. + */ + public static final int LOLLIPOP_MR1 = VERSION_CODES.LOLLIPOP_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 23.0. + */ + public static final int M = VERSION_CODES.M * SDK_INT_MULTIPLIER; + + /** + * Android 24.0. + */ + public static final int N = VERSION_CODES.N * SDK_INT_MULTIPLIER; + + /** + * Android 25.0. + */ + public static final int N_MR1 = VERSION_CODES.N_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 26.0. + */ + public static final int O = VERSION_CODES.O * SDK_INT_MULTIPLIER; + + /** + * Android 27.0. + */ + public static final int O_MR1 = VERSION_CODES.O_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 28.0. + */ + public static final int P = VERSION_CODES.P * SDK_INT_MULTIPLIER; + + /** + * Android 29.0. + */ + public static final int Q = VERSION_CODES.Q * SDK_INT_MULTIPLIER; + + /** + * Android 30.0. + */ + public static final int R = VERSION_CODES.R * SDK_INT_MULTIPLIER; + + /** + * Android 31.0. + */ + public static final int S = VERSION_CODES.S * SDK_INT_MULTIPLIER; + + /** + * Android 32.0. + */ + public static final int S_V2 = VERSION_CODES.S_V2 * SDK_INT_MULTIPLIER; + + /** + * Android 33.0. + */ + public static final int TIRAMISU = VERSION_CODES.TIRAMISU * SDK_INT_MULTIPLIER; + + /** + * Android 34.0. + */ + public static final int UPSIDE_DOWN_CAKE = + VERSION_CODES.UPSIDE_DOWN_CAKE * SDK_INT_MULTIPLIER; + + /** + * Android 35.0. + */ + public static final int VANILLA_ICE_CREAM = + VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER; } /** diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index ed4037c7d246..c18fb0c38b82 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -62,6 +62,12 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { @VisibleForTesting static final int FLAG_HAS_BINDERS = 1 << 12; + /** + * Indicates there may be intents with creator tokens contained in this bundle. When unparceled, + * they should be verified if tokens are missing or invalid. + */ + static final int FLAG_VERIFY_TOKENS_PRESENT = 1 << 13; + /** * Status when the Bundle can <b>assert</b> that the underlying Parcel DOES NOT contain @@ -274,6 +280,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { return orig; } + /** {@hide} */ + public void setIsIntentExtra() { + mFlags |= FLAG_VERIFY_TOKENS_PRESENT; + } + /** * Mark if this Bundle is okay to "defuse." That is, it's okay for system * processes to ignore any {@link BadParcelableException} encountered when diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java index 77d6cb762e06..f1d3957cc919 100644 --- a/core/java/android/os/CombinedVibration.java +++ b/core/java/android/os/CombinedVibration.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.os.vibrator.Flags; import android.util.SparseArray; @@ -28,6 +29,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.StringJoiner; +import java.util.function.Function; /** * A CombinedVibration describes a combination of haptic effects to be performed by one or more @@ -114,6 +116,17 @@ public abstract class CombinedVibration implements Parcelable { public abstract long getDuration(); /** + * Gets the estimated duration of the combined vibration in milliseconds. + * + * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns + * the estimated duration based on the {@link VibratorInfo}. For all other effects this will + * return the same as {@link #getDuration()}. + * + * @hide + */ + public abstract long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos); + + /** * Returns true if this effect could represent a touch haptic feedback. * * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified @@ -383,6 +396,23 @@ public abstract class CombinedVibration implements Parcelable { /** @hide */ @Override + public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) { + if (vibratorInfos == null) { + return getDuration(); + } + long maxDuration = 0; + for (int i = 0; i < vibratorInfos.size(); i++) { + long duration = mEffect.getDuration(vibratorInfos.valueAt(i)); + if ((duration == Long.MAX_VALUE) || (duration < 0)) { + return duration; + } + maxDuration = Math.max(maxDuration, duration); + } + return maxDuration; + } + + /** @hide */ + @Override public boolean isHapticFeedbackCandidate() { return mEffect.isHapticFeedbackCandidate(); } @@ -531,10 +561,27 @@ public abstract class CombinedVibration implements Parcelable { @Override public long getDuration() { + return getDuration(idx -> mEffects.valueAt(idx).getDuration()); + } + + /** @hide */ + @Override + public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) { + if (vibratorInfos == null) { + return getDuration(); + } + return getDuration(idx -> { + VibrationEffect effect = mEffects.valueAt(idx); + VibratorInfo info = vibratorInfos.get(mEffects.keyAt(idx)); + return effect.getDuration(info); + }); + } + + private long getDuration(Function<Integer, Long> durationFn) { long maxDuration = Long.MIN_VALUE; boolean hasUnknownStep = false; for (int i = 0; i < mEffects.size(); i++) { - long duration = mEffects.valueAt(i).getDuration(); + long duration = durationFn.apply(i); if (duration == Long.MAX_VALUE) { // If any duration is repeating, this combination duration is also repeating. return duration; @@ -750,12 +797,21 @@ public abstract class CombinedVibration implements Parcelable { @Override public long getDuration() { + return getDuration(CombinedVibration::getDuration); + } + + /** @hide */ + @Override + public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) { + return getDuration(effect -> effect.getDuration(vibratorInfos)); + } + + private long getDuration(Function<CombinedVibration, Long> durationFn) { boolean hasUnknownStep = false; long durations = 0; final int effectCount = mEffects.size(); for (int i = 0; i < effectCount; i++) { - CombinedVibration effect = mEffects.get(i); - long duration = effect.getDuration(); + long duration = durationFn.apply(mEffects.get(i)); if (duration == Long.MAX_VALUE) { // If any duration is repeating, this combination duration is also repeating. return duration; diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index a55398ac9752..ef1e6c9405f3 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -114,6 +114,7 @@ public final class Debug "opengl-tracing", "view-hierarchy", "support_boot_stages", + "app_info", }; /** @@ -1016,14 +1017,14 @@ public final class Debug // send VM_START. System.out.println("Waiting for debugger first packet"); - mWaiting = true; + setWaitingForDebugger(true); while (!isDebuggerConnected()) { try { Thread.sleep(100); } catch (InterruptedException ie) { } } - mWaiting = false; + setWaitingForDebugger(false); System.out.println("Debug.suspendAllAndSentVmStart"); VMDebug.suspendAllAndSendVmStart(); @@ -1049,12 +1050,12 @@ public final class Debug Chunk waitChunk = new Chunk(ChunkHandler.type("WAIT"), data, 0, 1); DdmServer.sendChunk(waitChunk); - mWaiting = true; + setWaitingForDebugger(true); while (!isDebuggerConnected()) { try { Thread.sleep(SPIN_DELAY); } catch (InterruptedException ie) {} } - mWaiting = false; + setWaitingForDebugger(false); System.out.println("Debugger has connected"); @@ -1112,6 +1113,16 @@ public final class Debug } /** + * Set whether the app is waiting for a debugger to connect + * + * @hide + */ + private static void setWaitingForDebugger(boolean waiting) { + mWaiting = waiting; + VMDebug.setWaitingForDebugger(waiting); + } + + /** * Returns an array of strings that identify Framework features. This is * used by DDMS to determine what sorts of operations the Framework can * perform. diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 2d3dd1b9383b..94768d111ca4 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -32,6 +32,8 @@ import android.text.TextUtils; import android.util.Log; import android.widget.Toast; +import com.android.internal.R; + import dalvik.system.VMRuntime; import java.io.BufferedReader; @@ -174,19 +176,6 @@ public class GraphicsEnvironment { nativeToggleAngleAsSystemDriver(enabled); } - /** - * Query to determine the ANGLE driver choice. - */ - private String queryAngleChoice(Context context, Bundle coreSettings, - String packageName) { - if (TextUtils.isEmpty(packageName)) { - Log.v(TAG, "No package name specified; use the system driver"); - return ANGLE_GL_DRIVER_CHOICE_DEFAULT; - } - - return queryAngleChoiceInternal(context, coreSettings, packageName); - } - private int getVulkanVersion(PackageManager pm) { // PackageManager doesn't have an API to retrieve the version of a specific feature, and we // need to avoid retrieving all system features here and looping through them. @@ -403,11 +392,13 @@ public class GraphicsEnvironment { * Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES; which corresponds to the * “angle_gl_driver_selection_pkgs” and “angle_gl_driver_selection_values” settings); if it * forces a choice. + * 3) The per-application ANGLE allowlist contained in the platform. This is an array of + * strings containing package names that should use ANGLE. */ - private String queryAngleChoiceInternal(Context context, Bundle bundle, - String packageName) { + private String queryAngleChoice(Context context, Bundle bundle, String packageName) { // Make sure we have a good package name if (TextUtils.isEmpty(packageName)) { + Log.v(TAG, "No package name specified; use the system driver"); return ANGLE_GL_DRIVER_CHOICE_DEFAULT; } @@ -436,8 +427,8 @@ public class GraphicsEnvironment { Log.v(TAG, " angle_gl_driver_selection_pkgs=" + optInPackages); Log.v(TAG, " angle_gl_driver_selection_values=" + optInValues); - // Make sure we have good settings to use - if (optInPackages.size() == 0 || optInPackages.size() != optInValues.size()) { + // Make sure we have valid settings, if any provided + if (optInPackages.size() != optInValues.size()) { Log.v(TAG, "Global.Settings values are invalid: " + "number of packages: " @@ -449,24 +440,53 @@ public class GraphicsEnvironment { // See if this application is listed in the per-application settings list final int pkgIndex = getPackageIndex(packageName, optInPackages); - - if (pkgIndex < 0) { - Log.v(TAG, packageName + " is not listed in per-application setting"); - return ANGLE_GL_DRIVER_CHOICE_DEFAULT; + if (pkgIndex >= 0) { + mAngleOptInIndex = pkgIndex; + + // The application IS listed in the per-application settings list; and so use the + // setting--choosing the current system driver if the setting is "default" + String optInValue = optInValues.get(pkgIndex); + Log.v( + TAG, + "ANGLE Developer option for '" + + packageName + + "' " + + "set to: '" + + optInValue + + "'"); + if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) { + return ANGLE_GL_DRIVER_CHOICE_ANGLE; + } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) { + return ANGLE_GL_DRIVER_CHOICE_NATIVE; + } } - mAngleOptInIndex = pkgIndex; - // The application IS listed in the per-application settings list; and so use the - // setting--choosing the current system driver if the setting is "default" - String optInValue = optInValues.get(pkgIndex); - Log.v(TAG, - "ANGLE Developer option for '" + packageName + "' " - + "set to: '" + optInValue + "'"); - if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) { - return ANGLE_GL_DRIVER_CHOICE_ANGLE; - } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) { - return ANGLE_GL_DRIVER_CHOICE_NATIVE; + Log.v(TAG, packageName + " is not listed in per-application setting"); + + // Check the per-device allowlist shipped in the platform + if (android.os.Flags.enableAngleAllowList()) { + String[] angleAllowListPackages = + context.getResources().getStringArray(R.array.config_angleAllowList); + + String allowListPackageList = String.join(" ", angleAllowListPackages); + Log.v(TAG, "ANGLE allowlist from config: " + allowListPackageList); + + for (String allowedPackage : angleAllowListPackages) { + if (allowedPackage.equals(packageName)) { + Log.v( + TAG, + "Package name " + + packageName + + " is listed in config_angleAllowList, enabling ANGLE"); + return ANGLE_GL_DRIVER_CHOICE_ANGLE; + } + } + Log.v( + TAG, + packageName + + " is not listed in ANGLE allowlist or settings, returning default"); } + // The user either chose default or an invalid value; go with the default driver. return ANGLE_GL_DRIVER_CHOICE_DEFAULT; } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 346ee7ca4f87..71d29af6cf02 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -41,6 +41,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; import com.android.sdksandbox.flags.Flags; +import dalvik.system.VMDebug; import dalvik.system.VMRuntime; import libcore.io.IoUtils; @@ -1410,6 +1411,7 @@ public class Process { public static void setArgV0(@NonNull String text) { sArgV0 = text; setArgV0Native(text); + VMDebug.setCurrentProcessName(text); } private static native void setArgV0Native(String text); diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index ffc58c537f2a..61dd11fd4122 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -55,6 +55,7 @@ import java.util.Locale; import java.util.Objects; import java.util.StringJoiner; import java.util.function.BiFunction; +import java.util.function.Function; /** * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}. @@ -565,6 +566,19 @@ public abstract class VibrationEffect implements Parcelable { public abstract long getDuration(); /** + * Gets the estimated duration of the segment for given vibrator, in milliseconds. + * + * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns + * the estimated duration based on the given {@link VibratorInfo}. For all other effects this + * will return the same as {@link #getDuration()}. + * + * @hide + */ + public long getDuration(@Nullable VibratorInfo vibratorInfo) { + return getDuration(); + } + + /** * Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended. * * <p>See {@link VibratorInfo#areVibrationFeaturesSupported(VibrationEffect)} for more @@ -904,13 +918,23 @@ public abstract class VibrationEffect implements Parcelable { @Override public long getDuration() { + return getDuration(VibrationEffectSegment::getDuration); + } + + /** @hide */ + @Override + public long getDuration(@Nullable VibratorInfo vibratorInfo) { + return getDuration(segment -> segment.getDuration(vibratorInfo)); + } + + private long getDuration(Function<VibrationEffectSegment, Long> durationFn) { if (mRepeatIndex >= 0) { return Long.MAX_VALUE; } int segmentCount = mSegments.size(); long totalDuration = 0; for (int i = 0; i < segmentCount; i++) { - long segmentDuration = mSegments.get(i).getDuration(); + long segmentDuration = durationFn.apply(mSegments.get(i)); if (segmentDuration < 0) { return segmentDuration; } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 6a2daeab5f0a..73a74b2f8abc 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -73,6 +73,9 @@ public class ZygoteProcess { private static final int ZYGOTE_CONNECT_TIMEOUT_MS = 60000; + // How long we wait for an AppZygote to complete pre-loading; this is a pretty conservative + // value that we can probably decrease over time, but we want to be careful here. + public static volatile int sAppZygotePreloadTimeoutMs = 15000; /** * Use a relatively short delay, because for app zygote, this is in the critical path of * service launch. @@ -1100,31 +1103,52 @@ public class ZygoteProcess { } /** + * Updates the timeout used when preloading code in the app-zygote + * + * @param timeoutMs timeout in milliseconds + */ + public static void setAppZygotePreloadTimeout(int timeoutMs) { + Slog.i(LOG_TAG, "Changing app-zygote preload timeout to " + timeoutMs + " ms."); + + sAppZygotePreloadTimeoutMs = timeoutMs; + } + + /** * Instructs the zygote to pre-load the application code for the given Application. * Only the app zygote supports this function. */ public boolean preloadApp(ApplicationInfo appInfo, String abi) throws ZygoteStartFailedEx, IOException { synchronized (mLock) { + int ret; ZygoteState state = openZygoteSocketIfNeeded(abi); - state.mZygoteOutputWriter.write("2"); - state.mZygoteOutputWriter.newLine(); + int previousSocketTimeout = state.mZygoteSessionSocket.getSoTimeout(); - state.mZygoteOutputWriter.write("--preload-app"); - state.mZygoteOutputWriter.newLine(); + try { + state.mZygoteSessionSocket.setSoTimeout(sAppZygotePreloadTimeoutMs); - // Zygote args needs to be strings, so in order to pass ApplicationInfo, - // write it to a Parcel, and base64 the raw Parcel bytes to the other side. - Parcel parcel = Parcel.obtain(); - appInfo.writeToParcel(parcel, 0 /* flags */); - String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall()); - parcel.recycle(); - state.mZygoteOutputWriter.write(encodedParcelData); - state.mZygoteOutputWriter.newLine(); + state.mZygoteOutputWriter.write("2"); + state.mZygoteOutputWriter.newLine(); - state.mZygoteOutputWriter.flush(); + state.mZygoteOutputWriter.write("--preload-app"); + state.mZygoteOutputWriter.newLine(); - return (state.mZygoteInputStream.readInt() == 0); + // Zygote args needs to be strings, so in order to pass ApplicationInfo, + // write it to a Parcel, and base64 the raw Parcel bytes to the other side. + Parcel parcel = Parcel.obtain(); + appInfo.writeToParcel(parcel, 0 /* flags */); + String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall()); + parcel.recycle(); + state.mZygoteOutputWriter.write(encodedParcelData); + state.mZygoteOutputWriter.newLine(); + + state.mZygoteOutputWriter.flush(); + + ret = state.mZygoteInputStream.readInt(); + } finally { + state.mZygoteSessionSocket.setSoTimeout(previousSocketTimeout); + } + return ret == 0; } } diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java index 39f841226e4e..b17e82a704bf 100644 --- a/core/java/android/os/vibrator/PrebakedSegment.java +++ b/core/java/android/os/vibrator/PrebakedSegment.java @@ -16,6 +16,17 @@ package android.os.vibrator; +import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK; +import static android.os.VibrationEffect.Composition.PRIMITIVE_THUD; +import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK; +import static android.os.VibrationEffect.EFFECT_CLICK; +import static android.os.VibrationEffect.EFFECT_DOUBLE_CLICK; +import static android.os.VibrationEffect.EFFECT_HEAVY_CLICK; +import static android.os.VibrationEffect.EFFECT_POP; +import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK; +import static android.os.VibrationEffect.EFFECT_THUD; +import static android.os.VibrationEffect.EFFECT_TICK; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -78,6 +89,32 @@ public final class PrebakedSegment extends VibrationEffectSegment { /** @hide */ @Override + public long getDuration(@Nullable VibratorInfo vibratorInfo) { + if (vibratorInfo == null) { + return getDuration(); + } + return switch (mEffectId) { + case EFFECT_TICK, + EFFECT_CLICK, + EFFECT_HEAVY_CLICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_CLICK); + case EFFECT_TEXTURE_TICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_TICK); + case EFFECT_THUD -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_THUD); + case EFFECT_DOUBLE_CLICK -> { + long clickDuration = vibratorInfo.getPrimitiveDuration(PRIMITIVE_CLICK); + yield clickDuration > 0 ? 2 * clickDuration : getDuration(); + } + default -> getDuration(); + }; + } + + private long estimateFromPrimitiveDuration(VibratorInfo vibratorInfo, int primitiveId) { + int duration = vibratorInfo.getPrimitiveDuration(primitiveId); + // Unsupported primitives should be ignored here. + return duration > 0 ? duration : getDuration(); + } + + /** @hide */ + @Override public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { if (vibratorInfo.isEffectSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) { return true; @@ -89,34 +126,30 @@ public final class PrebakedSegment extends VibrationEffectSegment { } // The vibrator does not have hardware support for the effect, but the effect has fallback // support. Check if a fallback will be available for the effect ID. - switch (mEffectId) { - case VibrationEffect.EFFECT_CLICK: - case VibrationEffect.EFFECT_DOUBLE_CLICK: - case VibrationEffect.EFFECT_HEAVY_CLICK: - case VibrationEffect.EFFECT_TICK: - // Any of these effects are always supported via some form of fallback. - return true; - default: - return false; - } + return switch (mEffectId) { + // Any of these effects are always supported via some form of fallback. + case EFFECT_CLICK, + EFFECT_DOUBLE_CLICK, + EFFECT_HEAVY_CLICK, + EFFECT_TICK -> true; + default -> false; + }; } /** @hide */ @Override public boolean isHapticFeedbackCandidate() { - switch (mEffectId) { - case VibrationEffect.EFFECT_CLICK: - case VibrationEffect.EFFECT_DOUBLE_CLICK: - case VibrationEffect.EFFECT_HEAVY_CLICK: - case VibrationEffect.EFFECT_POP: - case VibrationEffect.EFFECT_TEXTURE_TICK: - case VibrationEffect.EFFECT_THUD: - case VibrationEffect.EFFECT_TICK: - return true; - default: - // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback - return false; - } + return switch (mEffectId) { + case EFFECT_CLICK, + EFFECT_DOUBLE_CLICK, + EFFECT_HEAVY_CLICK, + EFFECT_POP, + EFFECT_TEXTURE_TICK, + EFFECT_THUD, + EFFECT_TICK -> true; + // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback + default -> false; + }; } /** @hide */ @@ -153,27 +186,25 @@ public final class PrebakedSegment extends VibrationEffectSegment { } private static boolean isValidEffectStrength(int strength) { - switch (strength) { - case VibrationEffect.EFFECT_STRENGTH_LIGHT: - case VibrationEffect.EFFECT_STRENGTH_MEDIUM: - case VibrationEffect.EFFECT_STRENGTH_STRONG: - return true; - default: - return false; - } + return switch (strength) { + case VibrationEffect.EFFECT_STRENGTH_LIGHT, + VibrationEffect.EFFECT_STRENGTH_MEDIUM, + VibrationEffect.EFFECT_STRENGTH_STRONG -> true; + default -> false; + }; } /** @hide */ @Override public void validate() { switch (mEffectId) { - case VibrationEffect.EFFECT_CLICK: - case VibrationEffect.EFFECT_DOUBLE_CLICK: - case VibrationEffect.EFFECT_HEAVY_CLICK: - case VibrationEffect.EFFECT_POP: - case VibrationEffect.EFFECT_TEXTURE_TICK: - case VibrationEffect.EFFECT_THUD: - case VibrationEffect.EFFECT_TICK: + case EFFECT_CLICK: + case EFFECT_DOUBLE_CLICK: + case EFFECT_HEAVY_CLICK: + case EFFECT_POP: + case EFFECT_TEXTURE_TICK: + case EFFECT_THUD: + case EFFECT_TICK: break; default: int[] ringtones = VibrationEffect.RINGTONES; diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java index 3c84bcda639b..91653edd1ba5 100644 --- a/core/java/android/os/vibrator/PrimitiveSegment.java +++ b/core/java/android/os/vibrator/PrimitiveSegment.java @@ -77,6 +77,16 @@ public final class PrimitiveSegment extends VibrationEffectSegment { /** @hide */ @Override + public long getDuration(@Nullable VibratorInfo vibratorInfo) { + if (vibratorInfo == null) { + return getDuration(); + } + int duration = vibratorInfo.getPrimitiveDuration(mPrimitiveId); + return duration > 0 ? duration + mDelay : getDuration(); + } + + /** @hide */ + @Override public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { return vibratorInfo.isPrimitiveSupported(mPrimitiveId); } diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java index e6e5a27bd731..88be96a4aef3 100644 --- a/core/java/android/os/vibrator/VibrationConfig.java +++ b/core/java/android/os/vibrator/VibrationConfig.java @@ -86,6 +86,7 @@ public class VibrationConfig { private final int mDefaultKeyboardVibrationIntensity; private final boolean mKeyboardVibrationSettingsSupported; + private final int mVibrationPipelineMaxDurationMs; /** @hide */ public VibrationConfig(@Nullable Resources resources) { @@ -106,6 +107,8 @@ public class VibrationConfig { com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger); mKeyboardVibrationSettingsSupported = loadBoolean(resources, com.android.internal.R.bool.config_keyboardVibrationSettingsSupported); + mVibrationPipelineMaxDurationMs = loadInteger(resources, + com.android.internal.R.integer.config_vibrationPipelineMaxDuration, 0); mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources, com.android.internal.R.integer.config_defaultAlarmVibrationIntensity); @@ -221,6 +224,23 @@ public class VibrationConfig { } /** + * The max duration, in milliseconds, allowed for pipelining vibration requests. + * + * <p>If the ongoing vibration duration is shorter than this threshold then it should be allowed + * to finish before the next vibration can start. If the ongoing vibration is longer than this + * then it should be cancelled when it's superseded for the new one. + * + * @return the max duration allowed for vibration effect to finish before the next request, or + * zero to disable effect pipelining. + */ + public int getVibrationPipelineMaxDurationMs() { + if (mVibrationPipelineMaxDurationMs < 0) { + return 0; + } + return mVibrationPipelineMaxDurationMs; + } + + /** * Whether or not vibrations are ignored if the device is on a wireless charger. * * <p>This may be the case if vibration during wireless charging causes unwanted results, like diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java index e1fb4e361008..dadc849dae0a 100644 --- a/core/java/android/os/vibrator/VibrationEffectSegment.java +++ b/core/java/android/os/vibrator/VibrationEffectSegment.java @@ -17,6 +17,7 @@ package android.os.vibrator; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -58,10 +59,23 @@ public abstract class VibrationEffectSegment implements Parcelable { */ public abstract long getDuration(); - /** - * Checks if a given {@link Vibrator} can play this segment as intended. See - * {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information about - * what counts as supported by a vibrator, and what counts as not. + /** + * Gets the estimated duration of the segment for given vibrator, in milliseconds. + * + * <p>For segments with hardware-dependent constants (e.g. primitives), this returns the + * estimated duration based on the given {@link VibratorInfo}. For all other effects this will + * return the same as {@link #getDuration()}. + * + * @hide + */ + public long getDuration(@Nullable VibratorInfo vibratorInfo) { + return getDuration(); + } + + /** + * Checks if a given {@link android.os.Vibrator} can play this segment as intended. See + * {@link android.os.Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more + * information about what counts as supported by a vibrator, and what counts as not. * * @hide */ diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index e3b1221b3004..7ceb948945fd 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -123,4 +123,25 @@ flag { metadata { purpose: PURPOSE_FEATURE } -}
\ No newline at end of file +} + +flag { + namespace: "haptics" + name: "primitive_composition_absolute_delay" + is_exported: true + description: "Enables functionality to create primitive compositions with absolute delays" + bug: "373357740" + metadata { + purpose: PURPOSE_FEATURE + } +} + +flag { + namespace: "haptics" + name: "vibration_pipeline_enabled" + description: "Enables functionality to pipeline vibration effects to avoid cancelling short vibrations" + bug: "344494220" + metadata { + purpose: PURPOSE_FEATURE + } +} diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index e59501f2742a..bfefba5b36e6 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -273,3 +273,12 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "platform_skin_temperature_enabled" + is_fixed_read_only: true + is_exported: true + namespace: "android_health_services" + description: "This fixed read-only flag is used to enable platform support for Skin Temperature." + bug: "369872443" +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 594005c3ebd6..0be55844b829 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12845,6 +12845,12 @@ public final class Settings { */ @Readable public static final String CONTEXTUAL_SEARCH_PACKAGE = "contextual_search_package"; + + /** + * Inetger property which determines whether advanced protection is on or not. + * @hide + */ + public static final String ADVANCED_PROTECTION_MODE = "advanced_protection_mode"; } /** diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java new file mode 100644 index 000000000000..59dd680f17be --- /dev/null +++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.advancedprotection; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.os.Binder; +import android.os.RemoteException; +import android.security.Flags; +import android.util.Log; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; + +/** + * <p>Advanced Protection is a mode that users can enroll their device into, that enhances security + * by enabling features and restrictions across both the platform and user apps. + * + * <p>This class provides methods to query and control the advanced protection mode + * for the device. + */ +@FlaggedApi(Flags.FLAG_AAPM_API) +@SystemService(Context.ADVANCED_PROTECTION_SERVICE) +public class AdvancedProtectionManager { + private static final String TAG = "AdvancedProtectionM"; + + private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback> + mCallbackMap = new ConcurrentHashMap<>(); + + @NonNull + private final IAdvancedProtectionService mService; + + /** @hide */ + public AdvancedProtectionManager(@NonNull IAdvancedProtectionService service) { + mService = service; + } + + /** + * Checks if advanced protection is enabled on the device. + * + * @return {@code true} if advanced protection is enabled, {@code false} otherwise. + */ + @RequiresPermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) + public boolean isAdvancedProtectionEnabled() { + try { + return mService.isAdvancedProtectionEnabled(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Registers a {@link Callback} to be notified of changes to the Advanced Protection state. + * + * <p>The provided callback will be called on the specified executor with the updated + * {@link AdvancedProtectionState}. Methods are called when the state changes, as well as once + * on initial registration. + * + * @param executor The executor of where the callback will execute. + * @param callback The {@link Callback} object to register.. + */ + @RequiresPermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) + public void registerAdvancedProtectionCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull Callback callback) { + if (mCallbackMap.get(callback) != null) { + Log.d(TAG, "registerAdvancedProtectionCallback callback already present"); + return; + } + + IAdvancedProtectionCallback delegate = new IAdvancedProtectionCallback.Stub() { + @Override + public void onAdvancedProtectionChanged(boolean enabled) { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onAdvancedProtectionChanged(enabled)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }; + + try { + mService.registerAdvancedProtectionCallback(delegate); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + mCallbackMap.put(callback, delegate); + } + + /** + * Unregister an existing {@link Callback}. + * + * @param callback The {@link Callback} object to unregister. + */ + @RequiresPermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) + public void unregisterAdvancedProtectionCallback(@NonNull Callback callback) { + IAdvancedProtectionCallback delegate = mCallbackMap.get(callback); + if (delegate == null) { + Log.d(TAG, "unregisterAdvancedProtectionCallback callback not present"); + return; + } + + try { + mService.unregisterAdvancedProtectionCallback(delegate); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + mCallbackMap.remove(callback); + } + + /** + * Enables or disables advanced protection on the device. + * + * @param enabled {@code true} to enable advanced protection, {@code false} to disable it. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE) + public void setAdvancedProtectionEnabled(boolean enabled) { + try { + mService.setAdvancedProtectionEnabled(enabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * A callback class for monitoring changes to Advanced Protection state + * + * <p>To register a callback, implement this interface, and register it with + * {@link AdvancedProtectionManager#registerAdvancedProtectionCallback(Executor, Callback)}. + * Methods are called when the state changes, as well as once on initial registration. + */ + @FlaggedApi(Flags.FLAG_AAPM_API) + public interface Callback { + /** + * Called when advanced protection state changes + * @param enabled the new state + */ + void onAdvancedProtectionChanged(boolean enabled); + } +} diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionCallback.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionCallback.aidl new file mode 100644 index 000000000000..828ce47e652b --- /dev/null +++ b/core/java/android/security/advancedprotection/IAdvancedProtectionCallback.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.advancedprotection; + +/** + * A callback class for monitoring changes to Advanced Protection state + * @hide + */ +oneway interface IAdvancedProtectionCallback { + void onAdvancedProtectionChanged(boolean enabled); +}
\ No newline at end of file diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl new file mode 100644 index 000000000000..ef0abf4f23a0 --- /dev/null +++ b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.advancedprotection; + +import android.security.advancedprotection.IAdvancedProtectionCallback; + +/** + * Binder interface for apps to communicate with system server implementations of + * AdvancedProtectionService. + * @hide + */ +interface IAdvancedProtectionService { + @EnforcePermission("QUERY_ADVANCED_PROTECTION_MODE") + boolean isAdvancedProtectionEnabled(); + @EnforcePermission("QUERY_ADVANCED_PROTECTION_MODE") + void registerAdvancedProtectionCallback(IAdvancedProtectionCallback callback); + @EnforcePermission("QUERY_ADVANCED_PROTECTION_MODE") + void unregisterAdvancedProtectionCallback(IAdvancedProtectionCallback callback); + @EnforcePermission("SET_ADVANCED_PROTECTION_MODE") + void setAdvancedProtectionEnabled(boolean enabled); +}
\ No newline at end of file diff --git a/core/java/android/security/forensic/IForensicService.aidl b/core/java/android/security/forensic/IForensicService.aidl new file mode 100644 index 000000000000..a944b18cb26d --- /dev/null +++ b/core/java/android/security/forensic/IForensicService.aidl @@ -0,0 +1,32 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.forensic; + +import android.security.forensic.IForensicServiceCommandCallback; +import android.security.forensic.IForensicServiceStateCallback; + +/** + * Binder interface to communicate with ForensicService. + * @hide + */ +interface IForensicService { + void monitorState(IForensicServiceStateCallback callback); + void makeVisible(IForensicServiceCommandCallback callback); + void makeInvisible(IForensicServiceCommandCallback callback); + void enable(IForensicServiceCommandCallback callback); + void disable(IForensicServiceCommandCallback callback); +} diff --git a/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl b/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl new file mode 100644 index 000000000000..7fa0c7f72690 --- /dev/null +++ b/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl @@ -0,0 +1,33 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.forensic; + +/** + * @hide + */ + oneway interface IForensicServiceCommandCallback { + @Backing(type="int") + enum ErrorCode{ + UNKNOWN = 0, + PERMISSION_DENIED = 1, + INVALID_STATE_TRANSITION = 2, + BACKUP_TRANSPORT_UNAVAILABLE = 3, + DATA_SOURCE_UNAVAILABLE = 3, + } + void onSuccess(); + void onFailure(ErrorCode error); + } diff --git a/core/java/android/security/forensic/IForensicServiceStateCallback.aidl b/core/java/android/security/forensic/IForensicServiceStateCallback.aidl new file mode 100644 index 000000000000..0cda35083ffd --- /dev/null +++ b/core/java/android/security/forensic/IForensicServiceStateCallback.aidl @@ -0,0 +1,31 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.forensic; + +/** + * @hide + */ + oneway interface IForensicServiceStateCallback { + @Backing(type="int") + enum State{ + UNKNOWN = 0, + INVISIBLE = 1, + VISIBLE = 2, + ENABLED = 3, + } + void onStateChange(State state); + } diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig index 5457bbee8ad3..b593902a95d4 100644 --- a/core/java/android/security/responsible_apis_flags.aconfig +++ b/core/java/android/security/responsible_apis_flags.aconfig @@ -33,7 +33,6 @@ flag { } } - flag { name: "content_uri_permission_apis" is_exported: true @@ -65,6 +64,14 @@ flag { } flag { + name: "aapm_api" + namespace: "responsible_apis" + description: "Android Advanced Protection Mode Service and Manager" + bug: "352420507" + is_fixed_read_only: true +} + +flag { name: "prevent_intent_redirect" namespace: "responsible_apis" description: "Prevent intent redirect attacks" diff --git a/core/java/android/service/wallpaper/Android.bp b/core/java/android/service/wallpaper/Android.bp deleted file mode 100644 index 552a07d97bf5..000000000000 --- a/core/java/android/service/wallpaper/Android.bp +++ /dev/null @@ -1,24 +0,0 @@ -package { - default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_", - // 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: "WallpaperSharedLib", - srcs: [ - "*.java", - "I*.aidl", - ], - - libs: ["unsupportedappusage"], - - // Enforce that the library is built against java 8 so that there are - // no compatibility issues with launcher - java_version: "1.8", -} diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 0dec13ff0c02..e254bf3e016f 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -73,8 +73,11 @@ import java.util.Locale; public abstract class Layout { // These should match the constants in framework/base/libs/hwui/hwui/DrawTextFunctor.h - private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 4f; - private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0.2f; + private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 0f; + private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0f; + private static final float HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP = 5f; + // since we're not using soft light yet, this needs to be much lower than the spec'd 0.8 + private static final float HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE = 0.5f; /** @hide */ @IntDef(prefix = { "BREAK_STRATEGY_" }, value = { @@ -1025,11 +1028,18 @@ public abstract class Layout { var padding = Math.max(HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX, mPaint.getTextSize() * HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR); + var cornerRadius = mPaint.density * HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP; + + // We set the alpha on the color itself instead of Paint.setAlpha(), because that function + // actually mutates the color in... *ehem* very strange ways. Also the color might get reset + // for various reasons, which also resets the alpha. + var white = Color.argb(HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE, 1f, 1f, 1f); + var black = Color.argb(HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE, 0f, 0f, 0f); var originalTextColor = mPaint.getColor(); var bgPaint = mWorkPlainPaint; bgPaint.reset(); - bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? Color.WHITE : Color.BLACK); + bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? white : black); bgPaint.setStyle(Paint.Style.FILL); int start = getLineStart(firstLine); @@ -1082,7 +1092,12 @@ public abstract class Layout { private void drawRect() { if (!mLineBackground.isEmpty()) { mLineBackground.inset(-padding, -padding); - canvas.drawRect(mLineBackground, bgPaint); + canvas.drawRoundRect( + mLineBackground, + cornerRadius, + cornerRadius, + bgPaint + ); } } @@ -1104,7 +1119,7 @@ public abstract class Layout { if (hasColorChanged) { mLastColor = textColor; - return isHighContrastTextDark(textColor) ? Color.WHITE : Color.BLACK; + return isHighContrastTextDark(textColor) ? white : black; } return bgPaint.getColor(); diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 9f54d9fca24b..3adbd686cd2c 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -24,7 +24,7 @@ import android.content.Context; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.Network; -import android.net.NetworkCapabilities; +import android.net.NetworkInfo; import android.net.SntpClient; import android.os.Build; import android.os.SystemClock; @@ -687,16 +687,8 @@ public abstract class NtpTrustedTime implements TrustedTime { if (connectivityManager == null) { return false; } - final NetworkCapabilities networkCapabilities = - connectivityManager.getNetworkCapabilities(network); - if (networkCapabilities == null) { - if (LOGD) Log.d(TAG, "getNetwork: failed to get network capabilities"); - return false; - } - final boolean isConnectedToInternet = networkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_INTERNET) - && networkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_VALIDATED); + final NetworkInfo ni = connectivityManager.getNetworkInfo(network); + // This connectivity check is to avoid performing a DNS lookup for the time server on a // unconnected network. There are races to obtain time in Android when connectivity // changes, which means that forceRefresh() can be called by various components before @@ -706,8 +698,8 @@ public abstract class NtpTrustedTime implements TrustedTime { // A side effect of check is that tests that run a fake NTP server on the device itself // will only be able to use it if the active network is connected, even though loopback // addresses are actually reachable. - if (!isConnectedToInternet) { - if (LOGD) Log.d(TAG, "getNetwork: no internet connectivity"); + if (ni == null || !ni.isConnected()) { + if (LOGD) Log.d(TAG, "getNetwork: no connectivity"); return false; } return true; diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index d08873c56e6a..59c66532fe0b 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -22,6 +22,7 @@ import static android.view.InsetsControllerProto.CONTROL; import static android.view.InsetsControllerProto.STATE; import static android.view.InsetsSource.ID_IME; import static android.view.InsetsSource.ID_IME_CAPTION_BAR; +import static android.view.ViewProtoLogGroups.IME_INSETS_CONTROLLER; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.LAST; import static android.view.WindowInsets.Type.all; @@ -69,6 +70,7 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.ImeTracing; import com.android.internal.inputmethod.SoftInputShowHideReason; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.function.TriFunction; import java.io.PrintWriter; @@ -1920,6 +1922,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final @InsetsType int requestedVisibleTypes = (mRequestedVisibleTypes & ~mask) | (visibleTypes & mask); if (mRequestedVisibleTypes != requestedVisibleTypes) { + ProtoLog.d(IME_INSETS_CONTROLLER, "Setting requestedVisibleTypes to %d (was %d)", + requestedVisibleTypes, mRequestedVisibleTypes); mRequestedVisibleTypes = requestedVisibleTypes; } } diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 326e34b258e5..bd6ff4c2af02 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -20,7 +20,6 @@ import static android.view.Display.DEFAULT_DISPLAY; import static java.lang.annotation.RetentionPolicy.SOURCE; -import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -36,8 +35,6 @@ import android.os.SystemClock; import android.util.Log; import android.util.SparseArray; -import com.android.hardware.input.Flags; - import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; @@ -4436,7 +4433,6 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see android.view.View#requestUnbufferedDispatch(int) * @see android.view.View#requestUnbufferedDispatch(MotionEvent) */ - @FlaggedApi(Flags.FLAG_POINTER_COORDS_IS_RESAMPLED_API) public boolean isResampled() { return isResampled; } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 83b4971c8621..82235d252a72 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -192,7 +192,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall boolean mDrawFinished = false; final Rect mScreenRect = new Rect(); - private final SurfaceSession mSurfaceSession = new SurfaceSession(); private final boolean mLimitedHdrEnabled = Flags.limitedHdr(); SurfaceControl mSurfaceControl; @@ -1549,7 +1548,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void createBlastSurfaceControls(ViewRootImpl viewRoot, String name, Transaction surfaceUpdateTransaction) { if (mSurfaceControl == null) { - mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) + mSurfaceControl = new SurfaceControl.Builder() .setName(name) .setLocalOwnerView(this) .setParent(viewRoot.updateAndGetBoundsLayer(surfaceUpdateTransaction)) @@ -1559,7 +1558,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } if (mBlastSurfaceControl == null) { - mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) + mBlastSurfaceControl = new SurfaceControl.Builder() .setName(name + "(BLAST)") .setLocalOwnerView(this) .setParent(mSurfaceControl) @@ -1577,7 +1576,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } if (mBackgroundControl == null) { - mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession) + mBackgroundControl = new SurfaceControl.Builder() .setName("Background for " + name) .setLocalOwnerView(this) .setOpaque(true) @@ -1734,9 +1733,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mRTLastReportedPosition.top /*positionTop*/, postScaleX, postScaleY); - mRTLastSetCrop.set((int) (clipLeft / postScaleX), (int) (clipTop / postScaleY), - (int) Math.ceil(clipRight / postScaleX), - (int) Math.ceil(clipBottom / postScaleY)); + mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom); if (DEBUG_POSITION) { Log.d(TAG, String.format("Setting layer crop = [%d, %d, %d, %d] " + "from scale %f, %f", mRTLastSetCrop.left, diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1b62cfdf5dcb..d9092ee737ce 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -29217,8 +29217,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, + " shadowX=" + shadowTouchPoint.x + " shadowY=" + shadowTouchPoint.y); } - final SurfaceSession session = new SurfaceSession(); - final SurfaceControl surfaceControl = new SurfaceControl.Builder(session) + final SurfaceControl surfaceControl = new SurfaceControl.Builder() .setName("drag surface") .setParent(root.getSurfaceControl()) .setBufferSize(shadowSize.x, shadowSize.y) @@ -29284,7 +29283,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (token == null) { surface.destroy(); } - session.kill(); surfaceControl.release(); } } diff --git a/core/java/android/view/ViewProtoLogGroups.java b/core/java/android/view/ViewProtoLogGroups.java new file mode 100644 index 000000000000..099f76189a50 --- /dev/null +++ b/core/java/android/view/ViewProtoLogGroups.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.NonNull; +import android.view.inputmethod.Flags; + +import com.android.internal.protolog.ProtoLogGroup; + +import java.util.UUID; + +/** + * Defines logging groups for ProtoLog. + * + * This file is used by the ProtoLogTool to generate optimized logging code. All of its dependencies + * must be included in services.core.wm.protologgroups build target. + * + * @hide + */ +final class ViewProtoLogGroups { + final static ProtoLogGroup IME_INSETS_CONTROLLER = new ProtoLogGroup( + "IME_INSETS_CONTROLLER", "InsetsController", Flags.refactorInsetsController()); + + final static ProtoLogGroup[] ALL_GROUPS = { + IME_INSETS_CONTROLLER + }; +} + diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3be9a821a463..0efded2d0eb9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -118,6 +118,7 @@ import static android.view.flags.Flags.disableDrawWakeLock; import static android.view.flags.Flags.sensitiveContentAppProtection; import static android.view.flags.Flags.sensitiveContentPrematureProtectionRemovedFix; import static android.view.flags.Flags.toolkitFrameRateFunctionEnablingReadOnly; +import static android.view.flags.Flags.toolkitFrameRateTouchBoost25q1; import static android.view.flags.Flags.toolkitFrameRateTypingReadOnly; import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly; import static android.view.flags.Flags.toolkitFrameRateViewEnablingReadOnly; @@ -279,6 +280,7 @@ import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneFallbackEventHandler; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.FastPrintWriter; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.RootViewSurfaceTaker; @@ -839,7 +841,6 @@ public final class ViewRootImpl implements ViewParent, * surfaces can ensure they do not draw into the surface inset region set by the parent window. */ private SurfaceControl mBoundsLayer; - private final SurfaceSession mSurfaceSession = new SurfaceSession(); private final Transaction mTransaction = new Transaction(); private final Transaction mFrameRateTransaction = new Transaction(); @@ -1282,6 +1283,8 @@ public final class ViewRootImpl implements ViewParent, mIsStylusPointerIconEnabled = InputSettings.isStylusPointerIconEnabled(mContext); + initializeProtoLogInProcess(); + String processorOverrideName = context.getResources().getString( R.string.config_inputEventCompatProcessorOverrideClassName); if (processorOverrideName.isEmpty()) { @@ -2708,7 +2711,7 @@ public final class ViewRootImpl implements ViewParent, */ public SurfaceControl updateAndGetBoundsLayer(Transaction t) { if (mBoundsLayer == null) { - mBoundsLayer = new SurfaceControl.Builder(mSurfaceSession) + mBoundsLayer = new SurfaceControl.Builder() .setContainerLayer() .setName("Bounds for - " + getTitle().toString()) .setParent(getSurfaceControl()) @@ -5949,7 +5952,34 @@ public final class ViewRootImpl implements ViewParent, // If no intersection, set bounds to empty. bounds.setEmpty(); } - return !bounds.isEmpty(); + + if (bounds.isEmpty()) { + return false; + } + + if (android.view.accessibility.Flags.focusRectMinSize()) { + adjustAccessibilityFocusedRectBoundsIfNeeded(bounds); + } + + return true; + } + + /** + * Adjusts accessibility focused rect bounds so that they are not invisible. + * + * <p>Focus bounds smaller than double the stroke width are very hard to see (or invisible). + * Expand the focus bounds if necessary to at least double the stroke width. + * @param bounds The bounds to adjust + */ + @VisibleForTesting + public void adjustAccessibilityFocusedRectBoundsIfNeeded(Rect bounds) { + final int minRectLength = mAccessibilityManager.getAccessibilityFocusStrokeWidth() * 2; + if (bounds.width() < minRectLength || bounds.height() < minRectLength) { + final float missingWidth = Math.max(0, minRectLength - bounds.width()); + final float missingHeight = Math.max(0, minRectLength - bounds.height()); + bounds.inset(-1 * (int) Math.ceil(missingWidth / 2), + -1 * (int) Math.ceil(missingHeight / 2)); + } } private Drawable getAccessibilityFocusedDrawable() { @@ -13079,6 +13109,11 @@ public final class ViewRootImpl implements ViewParent, boolean desiredAction = motionEventAction != MotionEvent.ACTION_OUTSIDE; boolean undesiredType = windowType == TYPE_INPUT_METHOD && sToolkitFrameRateTypingReadOnlyFlagValue; + + // don't suppress touch boost for TYPE_INPUT_METHOD in ViewRootImpl + if (toolkitFrameRateTouchBoost25q1()) { + return desiredAction && shouldEnableDvrr() && getFrameRateBoostOnTouchEnabled(); + } // use toolkitSetFrameRate flag to gate the change return desiredAction && !undesiredType && shouldEnableDvrr() && getFrameRateBoostOnTouchEnabled(); @@ -13403,4 +13438,13 @@ public final class ViewRootImpl implements ViewParent, mCurrentColorMode = colorMode; } + + private static boolean sProtoLogInitialized = false; + + private void initializeProtoLogInProcess() { + if (!sProtoLogInitialized) { + ProtoLog.init(ViewProtoLogGroups.ALL_GROUPS); + sProtoLogInitialized = true; + } + } } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 5129461095a3..65e993049979 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -86,7 +86,6 @@ public class WindowlessWindowManager implements IWindowSession { final HashMap<IBinder, ResizeCompleteCallback> mResizeCompletionForWindow = new HashMap<IBinder, ResizeCompleteCallback>(); - private final SurfaceSession mSurfaceSession = new SurfaceSession(); protected final SurfaceControl mRootSurface; private final Configuration mConfiguration; private final IWindowSession mRealWm; @@ -184,13 +183,13 @@ public class WindowlessWindowManager implements IWindowSession { InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame, float[] outSizeCompatScale) { - final SurfaceControl leash = new SurfaceControl.Builder(mSurfaceSession) + final SurfaceControl leash = new SurfaceControl.Builder() .setName(attrs.getTitle().toString() + "Leash") .setCallsite("WindowlessWindowManager.addToDisplay") .setParent(getParentSurface(window, attrs)) .build(); - final SurfaceControl sc = new SurfaceControl.Builder(mSurfaceSession) + final SurfaceControl sc = new SurfaceControl.Builder() .setFormat(attrs.format) .setBLASTLayer() .setName(attrs.getTitle().toString()) diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index c5ca059d1cea..60ccb77e1317 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -2288,10 +2288,7 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Gets the node bounds in window coordinates. * <p> - * When magnification is enabled, the bounds in window are scaled up by magnification scale - * and the positions are also adjusted according to the offset of magnification viewport. - * For example, it returns Rect(-180, -180, 0, 0) for original bounds Rect(10, 10, 100, 100), - * when the magnification scale is 2 and offsets for X and Y are both 200. + * The node bounds returned are not scaled by magnification. * <p/> * * @param outBounds The output node bounds. diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index 8ffae845de1f..820a1fba11ad 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -96,6 +96,16 @@ flag { flag { namespace: "accessibility" + name: "focus_rect_min_size" + description: "Ensures the a11y focus rect is big enough to be drawn as visible" + bug: "368667566" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + namespace: "accessibility" name: "force_invert_color" description: "Enable force force-dark for smart inversion and dark theme everywhere" bug: "282821643" diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig index 7b049278731d..c31df73fbeae 100644 --- a/core/java/android/view/flags/refresh_rate_flags.aconfig +++ b/core/java/android/view/flags/refresh_rate_flags.aconfig @@ -119,4 +119,12 @@ flag { description: "Feature flag to enable the fix for applyLegacyAnimation for VRR V QPR2" bug: "335874198" is_fixed_read_only: true +} + +flag { + name: "toolkit_frame_rate_touch_boost_25q1" + namespace: "toolkit" + description: "Feature flag to not suppress touch boost for specific windowTypes in VRR V QPR2" + bug: "335874198" + is_exported: true }
\ No newline at end of file diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 1e5c6d8177e1..47fc43735c4d 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2352,6 +2352,13 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note: * this does not return result of the request. For result use {@param resultReceiver} instead. + * + * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the + * Input Method is actually shown or hidden. If result is needed, use + * {@link android.view.WindowInsetsController#show} instead and set a + * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for + * the visibility of IME. If result is not needed, use {@link #showSoftInput(View, int)} + * instead. */ public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) { return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT); @@ -2399,6 +2406,14 @@ public final class InputMethodManager { & WindowInsets.Type.ime()) == 0) { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION); + if (resultReceiver != null) { + final boolean imeReqVisible = + (viewRootImpl.getInsetsController().getRequestedVisibleTypes() + & WindowInsets.Type.ime()) != 0; + resultReceiver.send( + imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN + : InputMethodManager.RESULT_SHOWN, null); + } // TODO(b/322992891) handle case of SHOW_IMPLICIT viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(), false /* fromIme */, statsToken); @@ -2531,6 +2546,13 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note: * this does not return result of the request. For result use {@param resultReceiver} instead. + * + * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the + * Input Method is actually shown or hidden. If result is needed, use + * {@link android.view.WindowInsetsController#hide} instead and set a + * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for + * the visibility of IME. If result is not needed, use + * {@link #hideSoftInputFromView(View, int)} instead. */ public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags, ResultReceiver resultReceiver) { @@ -2569,6 +2591,14 @@ public final class InputMethodManager { // TODO(b/322992891) handle case of HIDE_IMPLICIT_ONLY final var viewRootImpl = servedView.getViewRootImpl(); if (viewRootImpl != null) { + if (resultReceiver != null) { + final boolean imeReqVisible = + (viewRootImpl.getInsetsController().getRequestedVisibleTypes() + & WindowInsets.Type.ime()) != 0; + resultReceiver.send( + !imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_HIDDEN + : InputMethodManager.RESULT_HIDDEN, null); + } viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime()); } return true; diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig index bae8affcec47..aa4927ee9b9c 100644 --- a/core/java/android/view/inputmethod/flags.aconfig +++ b/core/java/android/view/inputmethod/flags.aconfig @@ -73,6 +73,17 @@ flag { } flag { + name: "consistent_get_current_input_method_info" + namespace: "input_method" + description: "Use BindingController as the source of truth in getCurrentInputMethodInfo" + bug: "355034523" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "ime_switcher_revamp" is_exported: true namespace: "input_method" diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index b7ee0b8a238a..877fa74138fe 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -16,6 +16,8 @@ package android.webkit; +import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Intent; @@ -25,6 +27,9 @@ import android.net.Uri; import android.os.Message; import android.view.View; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + public class WebChromeClient { /** @@ -547,18 +552,41 @@ public class WebChromeClient { * Parameters used in the {@link #onShowFileChooser} method. */ public static abstract class FileChooserParams { + /** @hide */ + @IntDef(prefix = { "MODE_" }, value = { + MODE_OPEN, + MODE_OPEN_MULTIPLE, + MODE_OPEN_FOLDER, + MODE_SAVE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Mode {} + /** Open single file. Requires that the file exists before allowing the user to pick it. */ public static final int MODE_OPEN = 0; /** Like Open but allows multiple files to be selected. */ public static final int MODE_OPEN_MULTIPLE = 1; - /** Like Open but allows a folder to be selected. The implementation should enumerate - all files selected by this operation. - This feature is not supported at the moment. - @hide */ + /** Like Open but allows a folder to be selected. */ + @FlaggedApi(android.webkit.Flags.FLAG_FILE_SYSTEM_ACCESS) public static final int MODE_OPEN_FOLDER = 2; /** Allows picking a nonexistent file and saving it. */ public static final int MODE_SAVE = 3; + /** @hide */ + @IntDef(prefix = { "PERMISSION_MODE_" }, value = { + PERMISSION_MODE_READ, + PERMISSION_MODE_READ_WRITE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionMode {} + + /** File or directory should be opened for reading only. */ + @FlaggedApi(android.webkit.Flags.FLAG_FILE_SYSTEM_ACCESS) + public static final int PERMISSION_MODE_READ = 0; + /** File or directory should be opened for read and write. */ + @FlaggedApi(android.webkit.Flags.FLAG_FILE_SYSTEM_ACCESS) + public static final int PERMISSION_MODE_READ_WRITE = 1; + /** * Parse the result returned by the file picker activity. This method should be used with * {@link #createIntent}. Refer to {@link #createIntent} for how to use it. @@ -585,6 +613,7 @@ public class WebChromeClient { /** * Returns file chooser mode. */ + @Mode public abstract int getMode(); /** @@ -616,6 +645,21 @@ public class WebChromeClient { public abstract String getFilenameHint(); /** + * Returns permission mode {@link #PERMISSION_MODE_READ} or + * {@link #PERMISSION_MODE_READ_WRITE} which indicates the intended mode for opening a file + * or directory. + * + * This can be used to determine whether an Intent such as + * {@link android.content.Intent#ACTION_OPEN_DOCUMENT} should be used rather than + * {@link android.content.Intent#ACTION_GET_CONTENT} to choose files. + */ + @FlaggedApi(Flags.FLAG_FILE_SYSTEM_ACCESS) + @PermissionMode + public int getPermissionMode() { + return PERMISSION_MODE_READ; + } + + /** * Creates an intent that would start a file picker for file selection. * The Intent supports choosing files from simple file sources available * on the device. Some advanced sources (for example, live media capture) diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index ffe8c80023c4..b6663992b851 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -17,6 +17,7 @@ package android.webkit; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1495,7 +1496,13 @@ public class WebView extends AbsoluteLayout * @param context Application Context. * @param callback will be called on the UI thread with {@code true} if initialization is * successful, {@code false} otherwise. + * @deprecated In WebView version 122.0.6174.0 and later, this initialization is done + * automatically, so there is no need to call this API. If called, this API will invoke + * the {@code callback} immediately with {@code true}, given that Safe Browsing + * is enabled and supported on the device. */ + @Deprecated + @FlaggedApi(android.webkit.Flags.FLAG_DEPRECATE_START_SAFE_BROWSING) public static void startSafeBrowsing(@NonNull Context context, @Nullable ValueCallback<Boolean> callback) { getFactory().getStatics().initSafeBrowsing(context, callback); diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig index d1013a92476f..c9e94d2f57f6 100644 --- a/core/java/android/webkit/flags.aconfig +++ b/core/java/android/webkit/flags.aconfig @@ -11,6 +11,14 @@ flag { } flag { + name: "deprecate_start_safe_browsing" + is_exported: true + namespace: "webview" + description: "Deprecating startSafeBrowsing API because it is a NOOP" + bug: "372193372" +} + +flag { name: "mainline_apis" is_exported: true namespace: "webview" @@ -26,3 +34,11 @@ flag { description: "New feature reduce user-agent for webview" bug: "371034303" } + +flag { + name: "file_system_access" + is_exported: true + namespace: "webview" + description: "New APIs required by File System Access" + bug: "40101963" +} diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java index 8e35843e2193..05dc910b89de 100644 --- a/core/java/android/window/DesktopModeFlags.java +++ b/core/java/android/window/DesktopModeFlags.java @@ -64,7 +64,9 @@ public enum DesktopModeFlags { ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true), ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS( Flags::enableDesktopWindowingTaskbarRunningApps, true), + // TODO: b/369763947 - remove this once ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS is ramped up ENABLE_DESKTOP_WINDOWING_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false), + ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false), ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false), ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS( Flags::enableWindowingTransitionHandlersObservers, false); diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 8e495ec1dc40..34abf3114925 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -525,6 +525,22 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Disables or enables activities to be started in adjacent tasks (see + * {@link FLAG_ACTIVITY_LAUNCH_ADJACENT}) for the specified root of any child tasks. This + * differs from {@link #setLaunchAdjacentFlagRoot(WindowContainerToken)} which controls the + * preferred launch-adjacent target and allows for selectively setting which root tasks can + * support launch-adjacent. + * @hide + */ + @NonNull + public WindowContainerTransaction setDisableLaunchAdjacent( + @NonNull WindowContainerToken container, boolean disabled) { + mHierarchyOps.add(HierarchyOp.createForSetDisableLaunchAdjacent(container.asBinder(), + disabled)); + return this; + } + + /** * Starts a task by id. The task is expected to already exist (eg. as a recent task). * @param taskId Id of task to start. * @param options bundle containing ActivityOptions for the task's top activity. @@ -1488,6 +1504,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION = 20; public static final int HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES = 21; public static final int HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE = 22; + public static final int HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT = 23; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -1556,6 +1573,8 @@ public final class WindowContainerTransaction implements Parcelable { private @InsetsType int mExcludeInsetsTypes; + private boolean mLaunchAdjacentDisabled; + public static HierarchyOp createForReparent( @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT) @@ -1644,6 +1663,15 @@ public final class WindowContainerTransaction implements Parcelable { .build(); } + /** Create a hierarchy op for disabling launch adjacent. */ + public static HierarchyOp createForSetDisableLaunchAdjacent(IBinder container, + boolean disabled) { + return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT) + .setContainer(container) + .setLaunchAdjacentDisabled(disabled) + .build(); + } + /** create a hierarchy op for deleting a task **/ public static HierarchyOp createForRemoveTask(@NonNull IBinder container) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REMOVE_TASK) @@ -1695,6 +1723,7 @@ public final class WindowContainerTransaction implements Parcelable { mReparentLeafTaskIfRelaunch = copy.mReparentLeafTaskIfRelaunch; mIsTrimmableFromRecents = copy.mIsTrimmableFromRecents; mExcludeInsetsTypes = copy.mExcludeInsetsTypes; + mLaunchAdjacentDisabled = copy.mLaunchAdjacentDisabled; } protected HierarchyOp(Parcel in) { @@ -1719,6 +1748,7 @@ public final class WindowContainerTransaction implements Parcelable { mReparentLeafTaskIfRelaunch = in.readBoolean(); mIsTrimmableFromRecents = in.readBoolean(); mExcludeInsetsTypes = in.readInt(); + mLaunchAdjacentDisabled = in.readBoolean(); } public int getType() { @@ -1814,13 +1844,11 @@ public final class WindowContainerTransaction implements Parcelable { } /** Denotes whether the parents should also be included in the op. */ - @NonNull public boolean includingParents() { return mIncludingParents; } - /** Set the task to be trimmable */ - @NonNull + /** Denotes whether the task can be trimmable from recents */ public boolean isTrimmableFromRecents() { return mIsTrimmableFromRecents; } @@ -1829,6 +1857,11 @@ public final class WindowContainerTransaction implements Parcelable { return mExcludeInsetsTypes; } + /** Denotes whether launch-adjacent flag is respected from this task or its children */ + public boolean isLaunchAdjacentDisabled() { + return mLaunchAdjacentDisabled; + } + /** Gets a string representation of a hierarchy-op type. */ public static String hopToString(int type) { switch (type) { @@ -1839,6 +1872,8 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "SetAdjacentRoot"; case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "LaunchTask"; case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "SetAdjacentFlagRoot"; + case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT: + return "SetDisableLaunchAdjacent"; case HIERARCHY_OP_TYPE_PENDING_INTENT: return "PendingIntent"; case HIERARCHY_OP_TYPE_START_SHORTCUT: return "StartShortcut"; case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: return "addInsetsFrameProvider"; @@ -1891,6 +1926,10 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: sb.append("container=").append(mContainer).append(" clearRoot=").append(mToTop); break; + case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT: + sb.append("container=").append(mContainer).append(" disabled=") + .append(mLaunchAdjacentDisabled); + break; case HIERARCHY_OP_TYPE_START_SHORTCUT: sb.append("options=").append(mLaunchOptions) .append(" info=").append(mShortcutInfo); @@ -1971,6 +2010,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeBoolean(mReparentLeafTaskIfRelaunch); dest.writeBoolean(mIsTrimmableFromRecents); dest.writeInt(mExcludeInsetsTypes); + dest.writeBoolean(mLaunchAdjacentDisabled); } @Override @@ -2047,6 +2087,8 @@ public final class WindowContainerTransaction implements Parcelable { private @InsetsType int mExcludeInsetsTypes; + private boolean mLaunchAdjacentDisabled; + Builder(int type) { mType = type; } @@ -2153,6 +2195,11 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + Builder setLaunchAdjacentDisabled(boolean disabled) { + mLaunchAdjacentDisabled = disabled; + return this; + } + HierarchyOp build() { final HierarchyOp hierarchyOp = new HierarchyOp(mType); hierarchyOp.mContainer = mContainer; @@ -2179,6 +2226,7 @@ public final class WindowContainerTransaction implements Parcelable { hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch; hierarchyOp.mIsTrimmableFromRecents = mIsTrimmableFromRecents; hierarchyOp.mExcludeInsetsTypes = mExcludeInsetsTypes; + hierarchyOp.mLaunchAdjacentDisabled = mLaunchAdjacentDisabled; return hierarchyOp; } diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 18129530978f..0bcc9c1f58c5 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -308,6 +308,13 @@ flag { } flag { + name: "enable_restore_to_previous_size_from_desktop_immersive" + namespace: "lse_desktop_experience" + description: "Restores the window bounds to their previous size when exiting desktop immersive" + bug: "372318163" +} + +flag { name: "enable_display_focus_in_shell_transitions" namespace: "lse_desktop_experience" description: "Creates a shell transition when display focus switches." @@ -334,3 +341,10 @@ flag { description: "Add new keyboard shortcut of moving a task into next display" bug: "364154795" } + +flag { + name: "enable_drag_to_maximize" + namespace: "lse_desktop_experience" + description: "Enables a switch to change the concequence of dragging a window to the top edge." + bug: "372614715" +} diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index f0ea7a82d763..b7012b68d459 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -108,3 +108,10 @@ flag { description: "Makes WindowLayoutInfo accessible without racing in the Activity#onCreate()" bug: "337820752" } + +flag { + namespace: "windowing_sdk" + name: "better_support_non_match_parent_activity" + description: "Relax the assumption of non-match parent activity" + bug: "356277166" +} diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index 18c8eb4ec46b..de7ad346a7cd 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -1195,7 +1195,12 @@ public class ResolverListAdapter extends BaseAdapter { @Nullable protected Drawable loadIconFromResource(Resources res, int resId) { - return res.getDrawableForDensity(resId, mIconDpi); + try { + return res.getDrawableForDensity(resId, mIconDpi); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Resource not found", e); + return null; + } } } diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java index dd6c879f1135..571fe0ba37b2 100644 --- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java +++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java @@ -96,6 +96,7 @@ public class ProtoLogViewerConfigReader { logger.log("Unloading viewer config hash " + hash); mLogMessageMap.remove(hash); } + mGroupHashes.remove(group); } } @@ -179,6 +180,6 @@ public class ProtoLogViewerConfigReader { } } - throw new RuntimeException("Group " + group + "not found in viewer config"); + throw new RuntimeException("Group " + group + " not found in viewer config"); } } diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java index 3e597d73f217..d3b1f972a955 100644 --- a/core/java/com/android/internal/widget/NotificationProgressBar.java +++ b/core/java/com/android/internal/widget/NotificationProgressBar.java @@ -20,16 +20,20 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification.ProgressStyle; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.graphics.drawable.LayerDrawable; import android.os.Bundle; import android.util.AttributeSet; +import android.util.Log; import android.view.RemotableViewMethod; import android.widget.ProgressBar; import android.widget.RemoteViews; import androidx.annotation.ColorInt; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.internal.widget.NotificationProgressDrawable.Part; @@ -49,7 +53,13 @@ import java.util.TreeSet; */ @RemoteViews.RemoteView public class NotificationProgressBar extends ProgressBar { + private static final String TAG = "NotificationProgressBar"; + private NotificationProgressModel mProgressModel; + + @Nullable + private List<Part> mProgressDrawableParts = null; + @Nullable private Drawable mProgressTrackerDrawable = null; @@ -58,7 +68,7 @@ public class NotificationProgressBar extends ProgressBar { } public NotificationProgressBar(Context context, AttributeSet attrs) { - this(context, attrs, com.android.internal.R.attr.progressBarStyle); + this(context, attrs, R.attr.progressBarStyle); } public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { @@ -82,10 +92,42 @@ public class NotificationProgressBar extends ProgressBar { "Bundle shouldn't be null"); mProgressModel = NotificationProgressModel.fromBundle(bundle); + + if (mProgressModel.isIndeterminate()) { + final int indeterminateColor = mProgressModel.getIndeterminateColor(); + setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor)); + } else { + mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(), + mProgressModel.getPoints(), + mProgressModel.getProgress(), mProgressModel.isStyledByProgress()); + + try { + final NotificationProgressDrawable drawable = getNotificationProgressDrawable(); + drawable.setParts(mProgressDrawableParts); + } catch (IllegalStateException ex) { + Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex); + } + } } - private void setProgressModel(@NonNull NotificationProgressModel model) { - mProgressModel = model; + @NonNull + private NotificationProgressDrawable getNotificationProgressDrawable() { + final Drawable d = getProgressDrawable(); + if (d == null) { + throw new IllegalStateException("getProgressDrawable() returns null"); + } + if (!(d instanceof LayerDrawable)) { + throw new IllegalStateException("getProgressDrawable() doesn't return a LayerDrawable"); + } + + final Drawable layer = ((LayerDrawable) d).findDrawableByLayerId(R.id.background); + if (!(layer instanceof NotificationProgressDrawable)) { + throw new IllegalStateException( + "Couldn't get NotificationProgressDrawable, retrieved drawable is: " + ( + layer != null ? layer.toString() : null)); + } + + return (NotificationProgressDrawable) layer; } /** @@ -97,7 +139,6 @@ public class NotificationProgressBar extends ProgressBar { public void setProgressTrackerIcon(@Nullable Icon icon) { } - /** * Async version of {@link #setProgressTrackerIcon} */ diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java index 89ef8759a169..2be7273e5d10 100644 --- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java +++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java @@ -45,6 +45,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Objects; /** @@ -156,11 +157,18 @@ public final class NotificationProgressDrawable extends Drawable { } /** - * + * Set the segments and points that constitute the drawable. */ - public void setParts(@NonNull Part... parts) { + public void setParts(List<Part> parts) { mParts.clear(); - mParts.addAll(Arrays.asList(parts)); + mParts.addAll(parts); + } + + /** + * Set the segments and points that constitute the drawable. + */ + public void setParts(@NonNull Part... parts) { + setParts(Arrays.asList(parts)); } @Override @@ -379,7 +387,7 @@ public final class NotificationProgressDrawable extends Drawable { if (state.mThemeAttrsPoints != null) { final TypedArray a = t.resolveAttributes( state.mThemeAttrsPoints, R.styleable.NotificationProgressDrawablePoints); - updateSegmentsFromTypedArray(a); + updatePointsFromTypedArray(a); a.recycle(); } } @@ -651,9 +659,11 @@ public final class NotificationProgressDrawable extends Drawable { State(@NonNull State orig, @Nullable Resources res) { mChangingConfigurations = orig.mChangingConfigurations; + mSegSegGap = orig.mSegSegGap; + mSegPointGap = orig.mSegPointGap; + mStrokeWidth = orig.mStrokeWidth; mStrokeColor = orig.mStrokeColor; mFadedStrokeColor = orig.mFadedStrokeColor; - mStrokeWidth = orig.mStrokeWidth; mStrokeDashWidth = orig.mStrokeDashWidth; mStrokeDashGap = orig.mStrokeDashGap; mPointRadius = orig.mPointRadius; @@ -791,6 +801,7 @@ public final class NotificationProgressDrawable extends Drawable { final State state = mState; mStrokePaint.setStrokeWidth(state.mStrokeWidth); + mDashedStrokePaint.setStrokeWidth(state.mStrokeWidth); if (state.mStrokeDashWidth != 0.0f) { final DashPathEffect e = new DashPathEffect( diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java index 98e6e8505534..adcc0f64b598 100644 --- a/core/java/com/android/internal/widget/NotificationRowIconView.java +++ b/core/java/com/android/internal/widget/NotificationRowIconView.java @@ -39,17 +39,24 @@ import com.android.internal.R; /** * An image view that holds the icon displayed at the start of a notification row. + * This can generally either display the "small icon" of a notification set via + * {@link this#setImageIcon(Icon)}, or an app icon controlled and fetched by the provider set + * through {@link this#setIconProvider(NotificationIconProvider)}. */ @RemoteViews.RemoteView public class NotificationRowIconView extends CachingIconView { + private NotificationIconProvider mIconProvider; + private boolean mApplyCircularCrop = false; private boolean mShouldShowAppIcon = false; + private Drawable mAppIcon = null; - // Padding and background set on the view prior to being changed by setShouldShowAppIcon(true), - // to be restored if shouldShowAppIcon becomes false again. + // Padding, background and colors set on the view prior to being overridden when showing the app + // icon, to be restored if we're showing the small icon again. private Rect mOriginalPadding = null; private Drawable mOriginalBackground = null; - + private int mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID; + private int mOriginalIconColor = ColoredIconHelper.COLOR_INVALID; public NotificationRowIconView(Context context) { super(context); @@ -81,6 +88,71 @@ public class NotificationRowIconView extends CachingIconView { super.onFinishInflate(); } + /** + * Sets the icon provider for this view. This is used to determine whether we should show the + * app icon instead of the small icon, and to fetch the app icon if needed. + */ + public void setIconProvider(NotificationIconProvider iconProvider) { + mIconProvider = iconProvider; + } + + private Drawable loadAppIcon() { + if (mIconProvider != null && mIconProvider.shouldShowAppIcon()) { + return mIconProvider.getAppIcon(); + } + return null; + } + + @RemotableViewMethod(asyncImpl = "setImageIconAsync") + @Override + public void setImageIcon(Icon icon) { + if (Flags.notificationsRedesignAppIcons()) { + if (mAppIcon != null) { + // We already know that we should be using the app icon, and we already loaded it. + // We assume that cannot change throughout the lifetime of a notification, so + // there's nothing to do here. + return; + } + mAppIcon = loadAppIcon(); + if (mAppIcon != null) { + setImageDrawable(mAppIcon); + adjustViewForAppIcon(); + } else { + super.setImageIcon(icon); + restoreViewForSmallIcon(); + } + return; + } + super.setImageIcon(icon); + } + + @RemotableViewMethod + @Override + public Runnable setImageIconAsync(Icon icon) { + if (Flags.notificationsRedesignAppIcons()) { + if (mAppIcon != null) { + // We already know that we should be using the app icon, and we already loaded it. + // We assume that cannot change throughout the lifetime of a notification, so + // there's nothing to do here. + return () -> { + }; + } + mAppIcon = loadAppIcon(); + if (mAppIcon != null) { + return () -> { + setImageDrawable(mAppIcon); + adjustViewForAppIcon(); + }; + } else { + return () -> { + super.setImageIcon(icon); + restoreViewForSmallIcon(); + }; + } + } + return super.setImageIconAsync(icon); + } + /** Whether the icon represents the app icon (instead of the small icon). */ @RemotableViewMethod public void setShouldShowAppIcon(boolean shouldShowAppIcon) { @@ -91,35 +163,122 @@ public class NotificationRowIconView extends CachingIconView { mShouldShowAppIcon = shouldShowAppIcon; if (mShouldShowAppIcon) { - if (mOriginalPadding == null && mOriginalBackground == null) { - mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(), - getPaddingRight(), getPaddingBottom()); - mOriginalBackground = getBackground(); - } - - setPadding(0, 0, 0, 0); - - // Make the background white in case the icon itself doesn't have one. - ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE, - PorterDuff.Mode.SRC_ATOP); - - if (mOriginalBackground == null) { - setBackground(getContext().getDrawable(R.drawable.notification_icon_circle)); - } - getBackground().mutate().setColorFilter(colorFilter); + adjustViewForAppIcon(); } else { // Restore original padding and background if needed - if (mOriginalPadding != null) { - setPadding(mOriginalPadding.left, mOriginalPadding.top, mOriginalPadding.right, - mOriginalPadding.bottom); - mOriginalPadding = null; - } - setBackground(mOriginalBackground); - mOriginalBackground = null; + restoreViewForSmallIcon(); } } } + /** + * Override padding and background from the view to display the app icon. + */ + private void adjustViewForAppIcon() { + removePadding(); + + if (Flags.notificationsUseAppIconInRow()) { + addWhiteBackground(); + } else { + // No need to set the background for notification redesign, since the icon + // factory already does that for us. + removeBackground(); + } + } + + /** + * Restore padding and background overridden by {@link this#adjustViewForAppIcon}. + * Does nothing if they were not overridden. + */ + private void restoreViewForSmallIcon() { + restorePadding(); + restoreBackground(); + restoreColors(); + } + + private void removePadding() { + if (mOriginalPadding == null) { + mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(), + getPaddingRight(), getPaddingBottom()); + } + setPadding(0, 0, 0, 0); + } + + private void restorePadding() { + if (mOriginalPadding != null) { + setPadding(mOriginalPadding.left, mOriginalPadding.top, + mOriginalPadding.right, + mOriginalPadding.bottom); + mOriginalPadding = null; + } + } + + private void removeBackground() { + if (mOriginalBackground == null) { + mOriginalBackground = getBackground(); + } + + setBackground(null); + } + + private void addWhiteBackground() { + if (mOriginalBackground == null) { + mOriginalBackground = getBackground(); + } + + // Make the background white in case the icon itself doesn't have one. + ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE, + PorterDuff.Mode.SRC_ATOP); + + if (mOriginalBackground == null) { + setBackground(getContext().getDrawable(R.drawable.notification_icon_circle)); + } + getBackground().mutate().setColorFilter(colorFilter); + } + + private void restoreBackground() { + // NOTE: This will not work if the original background was null, but that's better than + // accidentally clearing the background. We expect that there's generally going to be one + // anyway unless we manually clear it. + if (mOriginalBackground != null) { + setBackground(mOriginalBackground); + mOriginalBackground = null; + } + } + + private void restoreColors() { + if (mOriginalBackgroundColor != ColoredIconHelper.COLOR_INVALID) { + super.setBackgroundColor(mOriginalBackgroundColor); + mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID; + } + if (mOriginalIconColor != ColoredIconHelper.COLOR_INVALID) { + super.setOriginalIconColor(mOriginalIconColor); + mOriginalIconColor = ColoredIconHelper.COLOR_INVALID; + } + } + + @RemotableViewMethod + @Override + public void setBackgroundColor(int color) { + // Ignore color overrides if we're showing the app icon. + if (mAppIcon == null) { + super.setBackgroundColor(color); + } else { + mOriginalBackgroundColor = color; + } + } + + @RemotableViewMethod + @Override + public void setOriginalIconColor(int color) { + // Ignore color overrides if we're showing the app icon. + if (mAppIcon == null) { + super.setOriginalIconColor(color); + } else { + mOriginalIconColor = color; + } + } + @Nullable @Override Drawable loadSizeRestrictedIcon(@Nullable Icon icon) { @@ -197,4 +356,17 @@ public class NotificationRowIconView extends CachingIconView { return bitmap; } + + /** + * A provider that allows this view to verify whether it should use the app icon instead of the + * icon provided to it via setImageIcon, as well as actually fetching the app icon. It should + * primarily be called on the background thread. + */ + public interface NotificationIconProvider { + /** Whether this notification should use the app icon instead of the small icon. */ + boolean shouldShowAppIcon(); + + /** Get the app icon for this notification. */ + Drawable getAppIcon(); + } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 2bc32657bd4a..816ace2310a5 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -73,12 +73,6 @@ cc_library_shared_for_libandroid_runtime { srcs: [ "android_animation_PropertyValuesHolder.cpp", - "android_database_CursorWindow.cpp", - "android_database_SQLiteCommon.cpp", - "android_database_SQLiteConnection.cpp", - "android_database_SQLiteGlobal.cpp", - "android_database_SQLiteDebug.cpp", - "android_database_SQLiteRawStatement.cpp", "android_os_SystemClock.cpp", "android_os_SystemProperties.cpp", "android_os_Trace.cpp", @@ -163,6 +157,12 @@ cc_library_shared_for_libandroid_runtime { "android_opengl_GLES31.cpp", "android_opengl_GLES31Ext.cpp", "android_opengl_GLES32.cpp", + "android_database_CursorWindow.cpp", + "android_database_SQLiteCommon.cpp", + "android_database_SQLiteConnection.cpp", + "android_database_SQLiteGlobal.cpp", + "android_database_SQLiteDebug.cpp", + "android_database_SQLiteRawStatement.cpp", "android_graphics_GraphicBuffer.cpp", "android_graphics_SurfaceTexture.cpp", "android_view_CompositionSamplingListener.cpp", @@ -428,7 +428,6 @@ cc_library_shared_for_libandroid_runtime { "libnativehelper_jvm", "libpiex", "libpng", - "libsqlite", "libtiff_directory", "libui-types", "libutils", @@ -444,6 +443,12 @@ cc_library_shared_for_libandroid_runtime { host_linux: { srcs: [ "android_content_res_ApkAssets.cpp", + "android_database_CursorWindow.cpp", + "android_database_SQLiteCommon.cpp", + "android_database_SQLiteConnection.cpp", + "android_database_SQLiteGlobal.cpp", + "android_database_SQLiteDebug.cpp", + "android_database_SQLiteRawStatement.cpp", "android_hardware_input_InputApplicationHandle.cpp", "android_os_MessageQueue.cpp", "android_os_Parcel.cpp", @@ -459,6 +464,7 @@ cc_library_shared_for_libandroid_runtime { ], static_libs: [ "libbinderthreadstateutils", + "libsqlite", "libgui_window_info_static", ], shared_libs: [ diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index 18c314610bb2..c0e9215267e6 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -38,9 +38,7 @@ #define LOG_NDEBUG 1 #include <androidfw/CursorWindow.h> -#ifdef __linux__ #include "android_os_Parcel.h" -#endif #include "android_util_Binder.h" #include "android_database_SQLiteCommon.h" @@ -113,7 +111,6 @@ fail: return 0; } -#ifdef __linux__ static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); @@ -131,7 +128,6 @@ static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj window->getNumRows(), window->getNumColumns(), window); return reinterpret_cast<jlong>(window); } -#endif static void nativeDispose(JNIEnv* env, jclass clazz, jlong windowPtr) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); @@ -146,7 +142,6 @@ static jstring nativeGetName(JNIEnv* env, jclass clazz, jlong windowPtr) { return env->NewStringUTF(window->name().c_str()); } -#ifdef __linux__ static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jlong windowPtr, jobject parcelObj) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); @@ -159,7 +154,6 @@ static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jlong windowPtr, jniThrowRuntimeException(env, msg.c_str()); } } -#endif static void nativeClear(JNIEnv * env, jclass clazz, jlong windowPtr) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); @@ -526,35 +520,55 @@ static jboolean nativePutNull(JNIEnv* env, jclass clazz, jlong windowPtr, return true; } -static const JNINativeMethod sMethods[] = { - /* name, signature, funcPtr */ - {"nativeCreate", "(Ljava/lang/String;I)J", (void*)nativeCreate}, - {"nativeDispose", "(J)V", (void*)nativeDispose}, -#ifdef __linux__ - {"nativeCreateFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeCreateFromParcel}, - {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel}, -#endif - {"nativeGetName", "(J)Ljava/lang/String;", (void*)nativeGetName}, - {"nativeGetBlob", "(JII)[B", (void*)nativeGetBlob}, - {"nativeGetString", "(JII)Ljava/lang/String;", (void*)nativeGetString}, - {"nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V", - (void*)nativeCopyStringToBuffer}, - {"nativePutBlob", "(J[BII)Z", (void*)nativePutBlob}, - {"nativePutString", "(JLjava/lang/String;II)Z", (void*)nativePutString}, - - // ------- @FastNative below here ---------------------- - {"nativeClear", "(J)V", (void*)nativeClear}, - {"nativeGetNumRows", "(J)I", (void*)nativeGetNumRows}, - {"nativeSetNumColumns", "(JI)Z", (void*)nativeSetNumColumns}, - {"nativeAllocRow", "(J)Z", (void*)nativeAllocRow}, - {"nativeFreeLastRow", "(J)V", (void*)nativeFreeLastRow}, - {"nativeGetType", "(JII)I", (void*)nativeGetType}, - {"nativeGetLong", "(JII)J", (void*)nativeGetLong}, - {"nativeGetDouble", "(JII)D", (void*)nativeGetDouble}, - - {"nativePutLong", "(JJII)Z", (void*)nativePutLong}, - {"nativePutDouble", "(JDII)Z", (void*)nativePutDouble}, - {"nativePutNull", "(JII)Z", (void*)nativePutNull}, +static const JNINativeMethod sMethods[] = +{ + /* name, signature, funcPtr */ + { "nativeCreate", "(Ljava/lang/String;I)J", + (void*)nativeCreate }, + { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J", + (void*)nativeCreateFromParcel }, + { "nativeDispose", "(J)V", + (void*)nativeDispose }, + { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V", + (void*)nativeWriteToParcel }, + + { "nativeGetName", "(J)Ljava/lang/String;", + (void*)nativeGetName }, + { "nativeGetBlob", "(JII)[B", + (void*)nativeGetBlob }, + { "nativeGetString", "(JII)Ljava/lang/String;", + (void*)nativeGetString }, + { "nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V", + (void*)nativeCopyStringToBuffer }, + { "nativePutBlob", "(J[BII)Z", + (void*)nativePutBlob }, + { "nativePutString", "(JLjava/lang/String;II)Z", + (void*)nativePutString }, + + // ------- @FastNative below here ---------------------- + { "nativeClear", "(J)V", + (void*)nativeClear }, + { "nativeGetNumRows", "(J)I", + (void*)nativeGetNumRows }, + { "nativeSetNumColumns", "(JI)Z", + (void*)nativeSetNumColumns }, + { "nativeAllocRow", "(J)Z", + (void*)nativeAllocRow }, + { "nativeFreeLastRow", "(J)V", + (void*)nativeFreeLastRow }, + { "nativeGetType", "(JII)I", + (void*)nativeGetType }, + { "nativeGetLong", "(JII)J", + (void*)nativeGetLong }, + { "nativeGetDouble", "(JII)D", + (void*)nativeGetDouble }, + + { "nativePutLong", "(JJII)Z", + (void*)nativePutLong }, + { "nativePutDouble", "(JDII)Z", + (void*)nativePutDouble }, + { "nativePutNull", "(JII)Z", + (void*)nativePutNull }, }; int register_android_database_CursorWindow(JNIEnv* env) diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp index 68d49cda191d..a0987373687b 100644 --- a/core/jni/android_hardware_camera2_CameraMetadata.cpp +++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp @@ -353,10 +353,11 @@ static jbyteArray CameraMetadata_readValues(JNIEnv *env, jclass thiz, jint tag, if (entry.count == 0) { if (!metadata->exists(tag)) { ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag); + return NULL; } else { + // OK: we will return a 0-sized array. ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__, tag); } - return NULL; } jsize byteCount = entry.count * tagSize; diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp index bd403c7d9713..24551a4956a7 100644 --- a/core/jni/platform/host/HostRuntime.cpp +++ b/core/jni/platform/host/HostRuntime.cpp @@ -120,6 +120,7 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = { #endif {"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)}, {"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)}, +#ifdef __linux__ {"android.database.CursorWindow", REG_JNI(register_android_database_CursorWindow)}, {"android.database.sqlite.SQLiteConnection", REG_JNI(register_android_database_SQLiteConnection)}, @@ -127,7 +128,6 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = { {"android.database.sqlite.SQLiteDebug", REG_JNI(register_android_database_SQLiteDebug)}, {"android.database.sqlite.SQLiteRawStatement", REG_JNI(register_android_database_SQLiteRawStatement)}, -#ifdef __linux__ {"android.os.Binder", REG_JNI(register_android_os_Binder)}, {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)}, {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)}, diff --git a/core/res/Android.bp b/core/res/Android.bp index 17d7bfa40f90..aa324fcaca58 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -167,6 +167,7 @@ android_app { "android.os.flags-aconfig", "android.os.vibrator.flags-aconfig", "android.media.tv.flags-aconfig", + "android.security.flags-aconfig", "com.android.hardware.input.input-aconfig", ], } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5693d666adc2..6ab64768d9f0 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -852,6 +852,7 @@ <protected-broadcast android:name="android.service.ondeviceintelligence.MODEL_LOADED" /> <protected-broadcast android:name="android.service.ondeviceintelligence.MODEL_UNLOADED" /> + <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> <!-- ====================================================================== --> @@ -4105,6 +4106,24 @@ android:protectionLevel="signature|installer" /> <uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" /> + <!-- Allows an application to toggle the device's advanced protection mode status. + @FlaggedApi("android.security.aapm_api") + @SystemApi + @hide --> + <permission android:name="android.permission.SET_ADVANCED_PROTECTION_MODE" + android:protectionLevel="signature|privileged" + android:featureFlag="android.security.aapm_api"/> + <uses-permission android:name="android.permission.SET_ADVANCED_PROTECTION_MODE" + android:featureFlag="android.security.aapm_api"/> + + <!-- Allows an application to query the device's advanced protection mode status. + @FlaggedApi("android.security.aapm_api") --> + <permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" + android:protectionLevel="normal" + android:featureFlag="android.security.aapm_api"/> + <uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" + android:featureFlag="android.security.aapm_api"/> + <!-- @SystemApi @hide Allows an application to set a device owner on retail demo devices.--> <permission android:name="android.permission.PROVISION_DEMO_DEVICE" android:protectionLevel="signature|setup|knownSigner" @@ -8479,6 +8498,18 @@ android:protectionLevel="internal" android:featureFlag="android.content.pm.verification_service" /> + <!-- + This permission allows the system to receive PACKAGE_CHANGED broadcasts when the component + state of a non-exported component has been changed. + <p>Not for use by third-party applications. </p> + <p>Protection level: internal + @hide + --> + <permission + android:name="android.permission.RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED" + android:protectionLevel="internal" + android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml new file mode 100644 index 000000000000..8b2afa86986c --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/disabledAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorPrimary" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml new file mode 100644 index 000000000000..cefc9121b7a4 --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/primaryContentAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorOnPrimary" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml index 530d752732c1..eaf9e7d50bbd 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml +++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml @@ -1,6 +1,5 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2021 The Android Open Source Project + ~ Copyright (C) 2024 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -14,12 +13,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsKeyguardViewLegacy - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/udfps_animation_view_legacy" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <!-- Add fingerprint views here. See udfps_keyguard_view_internal.xml. --> - -</com.android.systemui.biometrics.UdfpsKeyguardViewLegacy> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/disabledAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorSurfaceContainer" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml new file mode 100644 index 000000000000..94e50fbe2533 --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/primaryContentAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorOnSurface" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml new file mode 100644 index 000000000000..0029de14e34a --- /dev/null +++ b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?attr/colorControlHighlight"> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/btn_material_filled_background_color"/> + <corners android:radius="?android:attr/buttonCornerRadius"/> + <size + android:width="@dimen/btn_material_width" + android:height="@dimen/btn_material_height" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml new file mode 100644 index 000000000000..105f077cd841 --- /dev/null +++ b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?attr/colorControlHighlight"> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/btn_material_filled_tonal_background_color"/> + <corners android:radius="?android:attr/buttonCornerRadius"/> + <size + android:width="@dimen/btn_material_width" + android:height="@dimen/btn_material_height" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_zen_mode_icon_piano.xml b/core/res/res/drawable/ic_zen_mode_icon_piano.xml new file mode 100644 index 000000000000..012b9398d687 --- /dev/null +++ b/core/res/res/drawable/ic_zen_mode_icon_piano.xml @@ -0,0 +1,25 @@ +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?android:attr/colorControlNormal" + android:viewportHeight="960" + android:viewportWidth="960"> + <path + android:fillColor="@android:color/white" + android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L330,760L330,580L320,580Q303,580 291.5,568.5Q280,557 280,540L280,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM630,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L680,200L680,540Q680,557 668.5,568.5Q657,580 640,580L630,580L630,760ZM390,760L570,760L570,580L560,580Q543,580 531.5,568.5Q520,557 520,540L520,200L440,200L440,540Q440,557 428.5,568.5Q417,580 400,580L390,580L390,760Z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/notification_progress.xml b/core/res/res/drawable/notification_progress.xml new file mode 100644 index 000000000000..3a6b60077045 --- /dev/null +++ b/core/res/res/drawable/notification_progress.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@id/background" + android:gravity="center_vertical|fill_horizontal"> + <com.android.internal.widget.NotificationProgressDrawable + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:segSegGap="@dimen/notification_progress_segSeg_gap" + android:segPointGap="@dimen/notification_progress_segPoint_gap"> + <segments + android:color="?attr/colorProgressBackgroundNormal" + android:dashGap="@dimen/notification_progress_segments_dash_gap" + android:dashWidth="@dimen/notification_progress_segments_dash_width" + android:width="@dimen/notification_progress_segments_height" /> + <points + android:color="?attr/colorProgressBackgroundNormal" + android:radius="@dimen/notification_progress_points_radius" + android:cornerRadius="@dimen/notification_progress_points_corner_radius" + android:inset="@dimen/notification_progress_points_inset" /> + </com.android.internal.widget.NotificationProgressDrawable> + </item> +</layer-list> diff --git a/core/res/res/layout/notification_template_material_progress.xml b/core/res/res/layout/notification_template_material_progress.xml index fdcefccdbf16..75827a279ff7 100644 --- a/core/res/res/layout/notification_template_material_progress.xml +++ b/core/res/res/layout/notification_template_material_progress.xml @@ -75,12 +75,11 @@ /> - <com.android.internal.widget.NotificationProgressBar - android:id="@+id/progress" + <include android:layout_width="0dp" - android:layout_height="@dimen/notification_progress_bar_height" - style="@style/Widget.Material.Light.ProgressBar.Horizontal" android:layout_weight="1" + android:layout_height="@dimen/notification_progress_tracker_height" + layout="@layout/notification_template_notification_progress_bar" /> <com.android.internal.widget.CachingIconView diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/layout/notification_template_notification_progress_bar.xml index f1c55ef16cdc..35748962cfb2 100644 --- a/packages/SystemUI/res/layout/udfps_bp_view.xml +++ b/core/res/res/layout/notification_template_notification_progress_bar.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2021 The Android Open Source Project + ~ Copyright (C) 2014 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. @@ -12,11 +12,12 @@ ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and - ~ limitations under the License. + ~ limitations under the License --> -<com.android.systemui.biometrics.UdfpsBpView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/udfps_animation_view" + +<com.android.internal.widget.NotificationProgressBar xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/progress" android:layout_width="match_parent" - android:layout_height="match_parent"> -</com.android.systemui.biometrics.UdfpsBpView> + android:layout_height="@dimen/notification_progress_tracker_height" + style="@style/Widget.Material.Notification.NotificationProgressBar" + /> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 8f4fbcd76939..9323276c2d5a 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -2122,7 +2122,7 @@ <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"לא ניתן היה לשחזר את קיצור הדרך עקב חוסר התאמה בחתימה על האפליקציות"</string> <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"לא ניתן היה לשחזר את קיצור הדרך"</string> <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"מקש הקיצור מושבת"</string> - <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"הסרת התקנה"</string> + <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"הסרה"</string> <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"לפתוח בכל זאת"</string> <string name="harmful_app_warning_title" msgid="8794823880881113856">"אותרה אפליקציה מזיקה"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> רוצה להציג חלקים מ-<xliff:g id="APP_2">%2$s</xliff:g>"</string> diff --git a/core/res/res/values-watch-v36/colors.xml b/core/res/res/values-watch-v36/colors.xml new file mode 100644 index 000000000000..4bc2a66fa206 --- /dev/null +++ b/core/res/res/values-watch-v36/colors.xml @@ -0,0 +1,18 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<!-- TODO(b/372524566): update color token's value to match material3 design. --> +<resources> +</resources>
\ No newline at end of file diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values-watch-v36/config.xml new file mode 100644 index 000000000000..c8f347afb318 --- /dev/null +++ b/core/res/res/values-watch-v36/config.xml @@ -0,0 +1,20 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <!-- Overrides system value --> + <dimen name="config_buttonCornerRadius">26dp</dimen> +</resources> diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml new file mode 100644 index 000000000000..ad3c1a3ef3a1 --- /dev/null +++ b/core/res/res/values-watch-v36/dimens_material.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources> + <!-- values for material3 button --> + <dimen name="btn_material_width">172dp</dimen> + <dimen name="btn_material_height">52dp</dimen> + <dimen name="btn_horizontal_edge_padding">14dp</dimen> + <dimen name="btn_drawable_padding">6dp</dimen> + <dimen name="btn_lineHeight">18sp</dimen> + <dimen name="btn_textSize">15sp</dimen> + + <!-- Opacity factor for disabled material3 widget --> + <dimen name="disabled_alpha_device_default">0.12</dimen> + <dimen name="primary_content_alpha_device_default">0.38</dimen> +</resources> diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml new file mode 100644 index 000000000000..32a22bb755cb --- /dev/null +++ b/core/res/res/values-watch-v36/styles_material.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <!-- Button Styles --> + <!-- Material Button - Filled --> + <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button"> + <item name="android:background">@drawable/btn_background_material_filled</item> + <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item> + </style> + + <!-- Material Button - Filled Tonal(Override system default button styles) --> + <style name="Widget.DeviceDefault.Button"> + <item name="background">@drawable/btn_background_material_filled_tonal</item> + <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item> + <item name="minHeight">@dimen/btn_material_height</item> + <item name="maxWidth">@dimen/btn_material_width</item> + <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item> + <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item> + <item name="android:drawablePadding">@dimen/btn_drawable_padding</item> + <item name="android:maxLines">2</item> + <item name="android:ellipsize">end</item> + <item name="android:breakStrategy">simple</item> + <item name="stateListAnimator">@anim/button_state_list_anim_material</item> + <item name="focusable">true</item> + <item name="clickable">true</item> + <item name="gravity">center_vertical</item> + </style> + + <!-- Text Styles --> + <!-- TextAppearance for Material Button - Filled --> + <style name="TextAppearance.Widget.Button.Material.Filled" parent="TextAppearance.Widget.Button.Material"> + <item name="textColor">@color/btn_material_filled_text_color</item> + </style> + + <!-- TextAppearance for Material Button - Filled Tonal --> + <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault"> + <item name="android:fontFamily">font-family-flex-device-default</item> + <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item> + <item name="textSize">@dimen/btn_textSize</item> + <item name="textColor">@color/btn_material_filled_tonal_text_color</item> + <item name="lineHeight">@dimen/btn_lineHeight</item> + </style> +</resources>
\ No newline at end of file diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8a2d767e9ad1..41981715a855 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -287,6 +287,11 @@ vibration params. --> <integer name="config_requestVibrationParamsTimeout">50</integer> + <!-- The max duration (in milliseconds) that the vibrator service will allow effects to be + pipelined (i.e. service will wait for ongoing vibration to finish instead of cancelling it + to start the new one). Value should be positive. Zero will disable effect pipelining. --> + <integer name="config_vibrationPipelineMaxDuration">25</integer> + <!-- Array containing the usages that should request vibration params before they are played. These usages don't have strong latency requirements, e.g. ringtone and notification, and can be slightly delayed. --> @@ -4447,17 +4452,25 @@ <string-array translatable="false" name="config_defaultPinnerServiceFiles"> </string-array> - <!-- True if camera app should be pinned via Pinner Service --> + <!-- Note: This config is deprecated, use config_pinnerCameraPinBytes instead. + True if camera app should be pinned via Pinner Service --> <bool name="config_pinnerCameraApp">false</bool> - <!-- Bytes that the PinnerService will pin for Home app --> - <integer name="config_pinnerHomePinBytes">0</integer> + <!-- Default: 60 MB. Bytes that the PinnerService will pin for Home app --> + <integer name="config_pinnerHomePinBytes">62914560</integer> + + <!-- Default: 80 MB. Bytes that the PinnerService will pin for Camera app --> + <integer name="config_pinnerCameraPinBytes">83886080</integer> - <!-- True if assistant app should be pinned via Pinner Service --> + <!-- Note: This config is deprecated, use config_pinnerAssistantPinBytes instead. + True if assistant app should be pinned via Pinner Service --> <bool name="config_pinnerAssistantApp">false</bool> - <!-- Bytes that the PinnerService will pin for WebView --> - <integer name="config_pinnerWebviewPinBytes">0</integer> + <!-- Default: 60 MB. Bytes that the PinnerService will pin for Assistant --> + <integer name="config_pinnerAssistantPinBytes">62914560</integer> + + <!-- Default: 20 MB. Bytes that the PinnerService will pin for WebView --> + <integer name="config_pinnerWebviewPinBytes">20971520</integer> <!-- Maximum memory that PinnerService will pin for apps expressed as a percentage of total device memory [0,100]. @@ -4857,7 +4870,7 @@ See android.credentials.CredentialManager --> <string name="config_defaultCredentialManagerHybridService" translatable="false"></string> - + <!-- The component name, flattened to a string, for the system's credential manager autofill service. This service allows interceding autofill requests and routing them to credential manager. @@ -5770,6 +5783,9 @@ of known compatibility issues. --> <string-array name="config_highRefreshRateBlacklist"></string-array> + <!-- The list of packages that will use ANGLE as the GLES driver --> + <string-array name="config_angleAllowList"></string-array> + <!-- The list of packages to automatically opt in to refresh rate suppressing by small area detection. Format of this array should be packageName:threshold and threshold value should be between 0 to 1--> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index b92aa2f355ed..7184d9a8c890 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -817,6 +817,22 @@ <dimen name="notification_progress_tracker_width">40dp</dimen> <!-- The size of the progress tracker height --> <dimen name="notification_progress_tracker_height">20dp</dimen> + <!-- The gap between segments in the notification progress bar --> + <dimen name="notification_progress_segSeg_gap">2dp</dimen> + <!-- The gap between a segment and a point in the notification progress bar --> + <dimen name="notification_progress_segPoint_gap">4dp</dimen> + <!-- The dash gap of the notification progress bar segments --> + <dimen name="notification_progress_segments_dash_gap">9dp</dimen> + <!-- The dash width of the notification progress bar segments --> + <dimen name="notification_progress_segments_dash_width">3dp</dimen> + <!-- The height of the notification progress bar segments --> + <dimen name="notification_progress_segments_height">6dp</dimen> + <!-- The radius of the notification progress bar points --> + <dimen name="notification_progress_points_radius">10dp</dimen> + <!-- The corner radius of the notification progress bar points drawn as rects --> + <dimen name="notification_progress_points_corner_radius">4dp</dimen> + <!-- The inset of the notification progress bar points drawn as rects --> + <dimen name="notification_progress_points_inset">0dp</dimen> <!-- The maximum size of the small notification icon on low memory devices. --> <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index eec6ae3fb521..cb8e4aa9b2a9 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -502,6 +502,10 @@ please see styles_device_defaults.xml. <style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" /> + <style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal"> + <item name="progressDrawable">@drawable/notification_progress</item> + </style> + <style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView"> <item name="lineHeight">20sp</item> <item name="textAppearance">@style/TextAppearance.Material.Notification</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 4f63fac96a8d..b4249557b4c9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2141,6 +2141,7 @@ <java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" /> <java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" /> <java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" /> + <java-symbol type="integer" name="config_vibrationPipelineMaxDuration" /> <java-symbol type="integer" name="config_radioScanningTimeout" /> <java-symbol type="integer" name="config_requestVibrationParamsTimeout" /> <java-symbol type="array" name="config_requestVibrationParamsForUsages" /> @@ -3471,6 +3472,8 @@ <java-symbol type="array" name="config_defaultPinnerServiceFiles" /> <java-symbol type="bool" name="config_pinnerCameraApp" /> <java-symbol type="integer" name="config_pinnerHomePinBytes" /> + <java-symbol type="integer" name="config_pinnerCameraPinBytes" /> + <java-symbol type="integer" name="config_pinnerAssistantPinBytes" /> <java-symbol type="bool" name="config_pinnerAssistantApp" /> <java-symbol type="integer" name="config_pinnerWebviewPinBytes" /> <java-symbol type="integer" name="config_pinnerMaxPinnedMemoryPercentage" /> @@ -3864,6 +3867,14 @@ <java-symbol type="dimen" name="notification_progress_icon_size" /> <java-symbol type="dimen" name="notification_progress_tracker_width" /> <java-symbol type="dimen" name="notification_progress_tracker_height" /> + <java-symbol type="dimen" name="notification_progress_segSeg_gap" /> + <java-symbol type="dimen" name="notification_progress_segPoint_gap" /> + <java-symbol type="dimen" name="notification_progress_segments_dash_gap" /> + <java-symbol type="dimen" name="notification_progress_segments_dash_width" /> + <java-symbol type="dimen" name="notification_progress_segments_height" /> + <java-symbol type="dimen" name="notification_progress_points_radius" /> + <java-symbol type="dimen" name="notification_progress_points_corner_radius" /> + <java-symbol type="dimen" name="notification_progress_points_inset" /> <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/> <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/> @@ -4448,6 +4459,7 @@ <java-symbol type="string" name="config_factoryResetPackage" /> <java-symbol type="array" name="config_highRefreshRateBlacklist" /> + <java-symbol type="array" name="config_angleAllowList" /> <java-symbol type="array" name="config_forceSlowJpegModeList" /> <java-symbol type="array" name="pause_wallpaper_render_when_state_change" /> <java-symbol type="bool" name="config_pauseWallpaperRenderWhenStateChangeEnabled" /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index d35bfb7bc1a1..352c3904406c 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -212,9 +212,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -314,9 +314,9 @@ easier. <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Material.NoActionBar"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -431,9 +431,9 @@ easier. <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -550,9 +550,9 @@ easier. <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -668,9 +668,9 @@ easier. <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -801,9 +801,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -911,9 +911,9 @@ easier. <style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -1027,9 +1027,9 @@ easier. <style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -1144,9 +1144,9 @@ easier. <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -1277,9 +1277,9 @@ easier. <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -1395,9 +1395,9 @@ easier. <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -1511,9 +1511,9 @@ easier. <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -1629,9 +1629,9 @@ easier. <style name="Theme.DeviceDefault.Panel" parent="Theme.Material.Panel"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -1746,9 +1746,9 @@ easier. <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Material.Wallpaper"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -1863,9 +1863,9 @@ easier. <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar" parent="Theme.Material.Wallpaper.NoTitleBar"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -1980,9 +1980,9 @@ easier. <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Material.InputMethod"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -2097,9 +2097,9 @@ easier. <style name="Theme.DeviceDefault.VoiceInteractionSession" parent="Theme.Material.VoiceInteractionSession"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -2218,9 +2218,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -2336,9 +2336,9 @@ easier. <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -2451,9 +2451,9 @@ easier. <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -2720,9 +2720,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -2821,9 +2821,9 @@ easier. <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Material.Light.DarkActionBar"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> - <item name="colorPrimaryDark">@color/primary_device_default_dark</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -2937,9 +2937,9 @@ easier. <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Material.Light.NoActionBar"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -3054,9 +3054,9 @@ easier. <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -3173,9 +3173,9 @@ easier. <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -3291,9 +3291,9 @@ easier. <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -3426,9 +3426,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -3535,9 +3535,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -3654,9 +3654,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -3774,9 +3774,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -3895,9 +3895,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -3996,9 +3996,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -4096,9 +4096,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -4217,9 +4217,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -4336,9 +4336,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -4454,9 +4454,9 @@ easier. <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Material.Light.Panel"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -4571,9 +4571,9 @@ easier. <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -4688,9 +4688,9 @@ easier. <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -4803,9 +4803,9 @@ easier. <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> - <item name="colorPrimaryDark">@color/primary_device_default_light</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -4937,7 +4937,7 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item> <item name="colorSecondary">@color/secondary_device_default_settings_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -5044,7 +5044,7 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item> <item name="colorSecondary">@color/secondary_device_default_settings_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -5148,7 +5148,7 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item> <item name="colorSecondary">@color/secondary_device_default_settings_light</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -5245,7 +5245,7 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item> <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item> @@ -5361,7 +5361,7 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item> <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -5487,7 +5487,7 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item> <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -5606,7 +5606,7 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item> <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item> @@ -5788,7 +5788,7 @@ easier. <style name="ThemeOverlay.DeviceDefault.Accent"> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> @@ -5863,7 +5863,7 @@ easier. <style name="ThemeOverlay.DeviceDefault.Accent.Light"> <item name="colorAccent">@color/accent_device_default_light</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> @@ -5942,7 +5942,7 @@ easier. <style name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" parent="ThemeOverlay.Material.Dark.ActionBar"> <item name="colorAccent">@color/accent_device_default_dark</item> - <item name="colorAccentPrimary">@color/system_primary_dark</item> + <item name="colorAccentPrimary">@color/accent_primary_device_default</item> <item name="colorAccentSecondary">@color/system_secondary_dark</item> <item name="colorAccentTertiary">@color/system_tertiary_dark</item> diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS index 5636f9bc436c..6d14ccb18c53 100644 --- a/core/tests/coretests/src/android/app/OWNERS +++ b/core/tests/coretests/src/android/app/OWNERS @@ -14,3 +14,5 @@ per-file KeyguardManagerTest.java = file:/services/core/java/com/android/server/ # Files related to background activity launches per-file Background*Start* = file:/BAL_OWNERS +# Files related to caching +per-file PropertyInvalidatedCache* = file:/PERFORMANCE_OWNERS diff --git a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java index 987f68d4f9e1..90ae306952fe 100644 --- a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java +++ b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java @@ -16,6 +16,9 @@ package android.content.pm.verify; +import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; +import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; @@ -23,12 +26,13 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningInfo; import android.content.pm.VersionedPackage; -import android.content.pm.verify.pkg.IVerificationSessionCallback; import android.content.pm.verify.pkg.IVerificationSessionInterface; import android.content.pm.verify.pkg.VerificationSession; import android.content.pm.verify.pkg.VerificationStatus; @@ -73,13 +77,12 @@ public class VerificationSessionTest { private static final long TEST_EXTEND_TIME = 2000L; private static final String TEST_KEY = "test key"; private static final String TEST_VALUE = "test value"; + private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; private final ArrayList<SharedLibraryInfo> mTestDeclaredLibraries = new ArrayList<>(); private final PersistableBundle mTestExtensionParams = new PersistableBundle(); @Mock private IVerificationSessionInterface mTestSessionInterface; - @Mock - private IVerificationSessionCallback mTestCallback; private VerificationSession mTestSession; @Before @@ -90,7 +93,7 @@ public class VerificationSessionTest { mTestExtensionParams.putString(TEST_KEY, TEST_VALUE); mTestSession = new VerificationSession(TEST_ID, TEST_INSTALL_SESSION_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, TEST_SIGNING_INFO, mTestDeclaredLibraries, - mTestExtensionParams, mTestSessionInterface, mTestCallback); + mTestExtensionParams, TEST_POLICY, mTestSessionInterface); } @Test @@ -118,6 +121,7 @@ public class VerificationSessionTest { // structure is different, but all the key/value pairs should be preserved as before. assertThat(sessionFromParcel.getExtensionParams().getString(TEST_KEY)) .isEqualTo(mTestExtensionParams.getString(TEST_KEY)); + assertThat(sessionFromParcel.getVerificationPolicy()).isEqualTo(TEST_POLICY); } @Test @@ -131,25 +135,60 @@ public class VerificationSessionTest { assertThat(mTestSession.extendTimeRemaining(TEST_EXTEND_TIME)).isEqualTo(TEST_EXTEND_TIME); verify(mTestSessionInterface, times(1)).extendTimeRemaining( eq(TEST_ID), eq(TEST_EXTEND_TIME)); - } - @Test - public void testCallback() throws Exception { PersistableBundle response = new PersistableBundle(); response.putString("test key", "test value"); final VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build(); mTestSession.reportVerificationComplete(status); - verify(mTestCallback, times(1)).reportVerificationComplete( + verify(mTestSessionInterface, times(1)).reportVerificationComplete( eq(TEST_ID), eq(status)); mTestSession.reportVerificationComplete(status, response); - verify(mTestCallback, times(1)) + verify(mTestSessionInterface, times(1)) .reportVerificationCompleteWithExtensionResponse( eq(TEST_ID), eq(status), eq(response)); final int reason = VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN; mTestSession.reportVerificationIncomplete(reason); - verify(mTestCallback, times(1)).reportVerificationIncomplete( + verify(mTestSessionInterface, times(1)).reportVerificationIncomplete( eq(TEST_ID), eq(reason)); } + + @Test + public void testPolicyNoOverride() { + assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY); + // This "set" is a no-op + assertThat(mTestSession.setVerificationPolicy(TEST_POLICY)).isTrue(); + assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY); + verifyZeroInteractions(mTestSessionInterface); + } + + @Test + public void testPolicyOverrideFail() throws Exception { + final int newPolicy = VERIFICATION_POLICY_BLOCK_FAIL_WARN; + when(mTestSessionInterface.setVerificationPolicy(anyInt(), anyInt())).thenReturn(false); + assertThat(mTestSession.setVerificationPolicy(newPolicy)).isFalse(); + verify(mTestSessionInterface, times(1)) + .setVerificationPolicy(eq(TEST_ID), eq(newPolicy)); + // Next "get" should not trigger binder call because the previous "set" has failed + assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY); + verifyNoMoreInteractions(mTestSessionInterface); + } + + @Test + public void testPolicyOverrideSuccess() throws Exception { + final int newPolicy = VERIFICATION_POLICY_BLOCK_FAIL_WARN; + when(mTestSessionInterface.setVerificationPolicy(anyInt(), anyInt())).thenReturn(true); + assertThat(mTestSession.setVerificationPolicy(newPolicy)).isTrue(); + verify(mTestSessionInterface, times(1)) + .setVerificationPolicy(eq(TEST_ID), eq(newPolicy)); + assertThat(mTestSession.getVerificationPolicy()).isEqualTo(newPolicy); + assertThat(mTestSession.getVerificationPolicy()).isEqualTo(newPolicy); + + // Setting back to the original policy should still trigger binder calls + assertThat(mTestSession.setVerificationPolicy(TEST_POLICY)).isTrue(); + verify(mTestSessionInterface, times(1)) + .setVerificationPolicy(eq(TEST_ID), eq(TEST_POLICY)); + assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY); + } } diff --git a/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java b/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java index 7f73a1eb4b48..56fc66a286c3 100644 --- a/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java +++ b/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java @@ -16,6 +16,8 @@ package android.content.pm.verify; +import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.eq; @@ -25,6 +27,7 @@ import static org.mockito.Mockito.when; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.SigningInfo; +import android.content.pm.verify.pkg.IVerificationSessionInterface; import android.content.pm.verify.pkg.IVerifierService; import android.content.pm.verify.pkg.VerificationSession; import android.content.pm.verify.pkg.VerifierService; @@ -52,6 +55,7 @@ public class VerifierServiceTest { private static final String TEST_PACKAGE_NAME = "com.foo"; private static final Uri TEST_PACKAGE_URI = Uri.parse("test://test"); private static final SigningInfo TEST_SIGNING_INFO = new SigningInfo(); + private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; private VerifierService mService; private VerificationSession mSession; @@ -60,8 +64,8 @@ public class VerifierServiceTest { mService = Mockito.mock(VerifierService.class, Answers.CALLS_REAL_METHODS); mSession = new VerificationSession(TEST_ID, TEST_INSTALL_SESSION_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, TEST_SIGNING_INFO, - new ArrayList<>(), - new PersistableBundle(), null, null); + new ArrayList<>(), new PersistableBundle(), TEST_POLICY, Mockito.mock( + IVerificationSessionInterface.class)); } @Test diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS index 1c0073417c9a..6149382d0800 100644 --- a/core/tests/coretests/src/android/os/OWNERS +++ b/core/tests/coretests/src/android/os/OWNERS @@ -9,3 +9,6 @@ per-file PowerManager*.java = file:/services/core/java/com/android/server/power/ # PerformanceHintManager per-file PerformanceHintManagerTest.java = file:/ADPF_OWNERS + +# Caching +per-file IpcDataCache* = file:/PERFORMANCE_OWNERS diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java index 25f9cb7c1088..c7d85d4b9b76 100644 --- a/core/tests/coretests/src/android/text/LayoutTest.java +++ b/core/tests/coretests/src/android/text/LayoutTest.java @@ -42,6 +42,7 @@ import android.text.Layout.Alignment; import android.text.style.ForegroundColorSpan; import android.text.style.StrikethroughSpan; +import androidx.annotation.NonNull; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -72,6 +73,7 @@ public class LayoutTest { private static final int STATIC_LINE_COUNT = 9; private static final int LINE_HEIGHT = 12; private static final int LINE_DESCENT = 4; + private static final int LINE_HEIGHT_TOLERANCE_PER_ITERATION = 3; private static final CharSequence LAYOUT_TEXT = "alwei\t;sdfs\ndf @"; private SpannableString mSpannedText; @@ -697,7 +699,7 @@ public class LayoutTest { if (drawCommand.path != null) { expect.that(drawCommand.path).isEqualTo(selectionPath); - expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW); + expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.YELLOW); expect.that(drawCommand.paint.getBlendMode()).isNotNull(); highlightsFound++; } else if (drawCommand.text != null) { @@ -750,7 +752,7 @@ public class LayoutTest { if (drawCommand.path != null) { expect.that(drawCommand.path).isEqualTo(selectionPath); - expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW); + expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.YELLOW); expect.that(drawCommand.paint.getBlendMode()).isNotNull(); highlightsFound++; } else if (drawCommand.text != null) { @@ -802,7 +804,7 @@ public class LayoutTest { if (drawCommand.path != null) { expect.that(drawCommand.path).isEqualTo(selectionPath); - expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN); + expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.CYAN); expect.that(drawCommand.paint.getBlendMode()).isNull(); highlightsFound++; } else if (drawCommand.text != null) { @@ -855,7 +857,7 @@ public class LayoutTest { if (drawCommand.path != null) { expect.that(drawCommand.path).isEqualTo(selectionPath); - expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN); + expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.CYAN); expect.that(drawCommand.paint.getBlendMode()).isNull(); highlightsFound++; } else if (drawCommand.text != null) { @@ -914,10 +916,11 @@ public class LayoutTest { if (drawCommand.rect != null) { numBackgroundsFound++; - expect.that(drawCommand.paint.getColor()).isEqualTo(Color.BLACK); + expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.BLACK); expect.that(drawCommand.rect.height()).isAtLeast(LINE_HEIGHT); expect.that(drawCommand.rect.width()).isGreaterThan(0); - float expectedY = (numBackgroundsFound) * (LINE_HEIGHT + LINE_DESCENT); + float expectedY = numBackgroundsFound + * (LINE_HEIGHT + LINE_DESCENT - LINE_HEIGHT_TOLERANCE_PER_ITERATION); expect.that(drawCommand.rect.bottom).isAtLeast(expectedY); } else if (drawCommand.text != null) { // draw text @@ -997,20 +1000,38 @@ public class LayoutTest { .filter(it -> it.rect != null) .toList(); - expect.that(backgroundCommands.get(0).paint.getColor()).isEqualTo(Color.BLACK); - expect.that(backgroundCommands.get(1).paint.getColor()).isEqualTo(Color.WHITE); - expect.that(backgroundCommands.get(2).paint.getColor()).isEqualTo(Color.WHITE); - expect.that(backgroundCommands.get(3).paint.getColor()).isEqualTo(Color.WHITE); - expect.that(backgroundCommands.get(4).paint.getColor()).isEqualTo(Color.WHITE); - expect.that(backgroundCommands.get(5).paint.getColor()).isEqualTo(Color.BLACK); - expect.that(backgroundCommands.get(6).paint.getColor()).isEqualTo(Color.BLACK); - expect.that(backgroundCommands.get(7).paint.getColor()).isEqualTo(Color.BLACK); - expect.that(backgroundCommands.get(8).paint.getColor()).isEqualTo(Color.BLACK); - expect.that(backgroundCommands.get(9).paint.getColor()).isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(0).paint.getColor())) + .isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(1).paint.getColor())) + .isEqualTo(Color.WHITE); + expect.that(removeAlpha(backgroundCommands.get(2).paint.getColor())) + .isEqualTo(Color.WHITE); + expect.that(removeAlpha(backgroundCommands.get(3).paint.getColor())) + .isEqualTo(Color.WHITE); + expect.that(removeAlpha(backgroundCommands.get(4).paint.getColor())) + .isEqualTo(Color.WHITE); + expect.that(removeAlpha(backgroundCommands.get(5).paint.getColor())) + .isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(6).paint.getColor())) + .isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(7).paint.getColor())) + .isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(8).paint.getColor())) + .isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(9).paint.getColor())) + .isEqualTo(Color.BLACK); expect.that(backgroundCommands.size()).isEqualTo(backgroundRectsDrawn); } + private int removeAlpha(int color) { + return Color.rgb( + Color.red(color), + Color.green(color), + Color.blue(color) + ); + } + private static final class MockCanvas extends Canvas { static class DrawCommand { @@ -1122,6 +1143,11 @@ public class LayoutTest { mDrawCommands.add(new DrawCommand(rect, p)); } + @Override + public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) { + mDrawCommands.add(new DrawCommand(rect, paint)); + } + List<DrawCommand> getDrawCommands() { return mDrawCommands; } diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java index c98142357d69..483ebc2c8649 100644 --- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java +++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java @@ -24,6 +24,7 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_ANIMATION_BUGFIX_25Q1; import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY; +import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_TOUCH_BOOST_25Q1; import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY; import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY; import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API; @@ -118,6 +119,67 @@ public class ViewFrameRateTest { } @Test + @RequiresFlagsEnabled(FLAG_TOOLKIT_FRAME_RATE_TOUCH_BOOST_25Q1) + public void shouldNotSuppressTouchBoost() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } + + mActivityRule.runOnUiThread(() -> { + ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; + layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; + mMovingView.setLayoutParams(layoutParams); + mMovingView.setOnClickListener((v) -> {}); + }); + waitForFrameRateCategoryToSettle(); + + int[] position = new int[2]; + mActivityRule.runOnUiThread(() -> { + mMovingView.getLocationOnScreen(position); + position[0] += mMovingView.getWidth() / 2; + position[1] += mMovingView.getHeight() / 2; + }); + final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + + // update the window type to TYPE_INPUT_METHOD + int windowType = mViewRoot.mWindowAttributes.type; + final WindowManager.LayoutParams attrs = mViewRoot.mWindowAttributes; + attrs.type = TYPE_INPUT_METHOD; + instrumentation.runOnMainSync(() -> { + mViewRoot.setLayoutParams(attrs, false); + }); + instrumentation.waitForIdleSync(); + + final WindowManager.LayoutParams newAttrs = mViewRoot.mWindowAttributes; + assertTrue(newAttrs.type == TYPE_INPUT_METHOD); + + long now = SystemClock.uptimeMillis(); + MotionEvent down = MotionEvent.obtain( + now, // downTime + now, // eventTime + MotionEvent.ACTION_DOWN, // action + position[0], // x + position[1], // y + 0 // metaState + ); + down.setSource(InputDevice.SOURCE_TOUCHSCREEN); + instrumentation.sendPointerSync(down); + down.recycle(); + + // should have touch boost + assertTrue(mViewRoot.getIsTouchBoosting()); + + // Reset the window type back to the original one. + newAttrs.type = windowType; + instrumentation.runOnMainSync(() -> { + mViewRoot.setLayoutParams(newAttrs, false); + }); + instrumentation.waitForIdleSync(); + assertTrue(mViewRoot.mWindowAttributes.type == windowType); + } + + @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY) public void inputMethodWithContentMoves() throws Throwable { if (!ViewProperties.vrr_enabled().orElse(true)) { diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 632721126714..ed9fc1c9e547 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -71,6 +71,7 @@ import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; import android.os.Binder; import android.os.SystemProperties; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; @@ -82,6 +83,7 @@ import android.util.DisplayMetrics; import android.util.Log; import android.view.WindowInsets.Side; import android.view.WindowInsets.Type; +import android.view.accessibility.AccessibilityManager; import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -1628,6 +1630,42 @@ public class ViewRootImplTest { }); } + @Test + @EnableFlags(android.view.accessibility.Flags.FLAG_FOCUS_RECT_MIN_SIZE) + public void testAdjustAccessibilityFocusedBounds_largeEnoughBoundsAreUnchanged() { + final int strokeWidth = sContext.getSystemService(AccessibilityManager.class) + .getAccessibilityFocusStrokeWidth(); + final int left, top, width, height; + left = top = 100; + width = height = strokeWidth * 2; + final Rect bounds = new Rect(left, top, left + width, top + height); + final Rect originalBounds = new Rect(bounds); + + mViewRootImpl.adjustAccessibilityFocusedRectBoundsIfNeeded(bounds); + + assertThat(bounds).isEqualTo(originalBounds); + } + + @Test + @EnableFlags(android.view.accessibility.Flags.FLAG_FOCUS_RECT_MIN_SIZE) + public void testAdjustAccessibilityFocusedBounds_smallBoundsAreExpanded() { + final int strokeWidth = sContext.getSystemService(AccessibilityManager.class) + .getAccessibilityFocusStrokeWidth(); + final int left, top, width, height; + left = top = 100; + width = height = strokeWidth; + final Rect bounds = new Rect(left, top, left + width, top + height); + final Rect originalBounds = new Rect(bounds); + + mViewRootImpl.adjustAccessibilityFocusedRectBoundsIfNeeded(bounds); + + // Bounds should be centered on the same point, but expanded to at least strokeWidth * 2 + assertThat(bounds.centerX()).isEqualTo(originalBounds.centerX()); + assertThat(bounds.centerY()).isEqualTo(originalBounds.centerY()); + assertThat(bounds.width()).isAtLeast(strokeWidth * 2); + assertThat(bounds.height()).isAtLeast(strokeWidth * 2); + } + private boolean setForceDarkSysProp(boolean isForceDarkEnabled) { try { SystemProperties.set( diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java index 6419c1e07f2e..79a478a7676b 100644 --- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java @@ -254,7 +254,6 @@ public class NotificationProgressBarTest { // Colors with 50% opacity int fadedGreen = 0x7F00FF00; - int fadedBlue = 0x7F0000FF; int fadedYellow = 0x7FFFFF00; List<Part> expected = new ArrayList<>(List.of( diff --git a/core/tests/vibrator/src/android/os/CombinedVibrationTest.java b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java index 244fcff7d27d..37ddfd21c98d 100644 --- a/core/tests/vibrator/src/android/os/CombinedVibrationTest.java +++ b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java @@ -22,6 +22,9 @@ import static junit.framework.Assert.assertTrue; import static org.testng.Assert.assertThrows; +import android.hardware.vibrator.IVibrator; +import android.util.SparseArray; + import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -134,6 +137,54 @@ public class CombinedVibrationTest { } @Test + public void testDurationMono_withVibratorSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + // Use max duration from all vibrators. + assertEquals(10, CombinedVibration.createParallel( + VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration(infos)); + assertEquals(111, CombinedVibration.createParallel( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .getDuration(infos)); + } + + @Test + public void testDurationMono_withVibratorNotSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + // Use max duration from all vibrators. + assertEquals(-1, CombinedVibration.createParallel( + VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration(infos)); + assertEquals(-1, CombinedVibration.createParallel( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .getDuration(infos)); + } + + @Test public void testDurationStereo() { assertEquals(6, CombinedVibration.startParallel() .addVibrator(1, VibrationEffect.createOneShot(1, 1)) @@ -156,6 +207,75 @@ public class CombinedVibrationTest { } @Test + public void testDurationStereo_withVibratorSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + // Use specific vibrator durations, then max effect duration + assertEquals(111, CombinedVibration.startParallel() + .addVibrator(1, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .addVibrator(2, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + assertEquals(110, CombinedVibration.startParallel() + .addVibrator(1, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + } + + @Test + public void testDurationStereo_withVibratorNotSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + // One vibrator does not support primitives + assertEquals(-1, CombinedVibration.startParallel() + .addVibrator(1, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .addVibrator(2, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + // Invalid vibrator ID + assertEquals(-1, CombinedVibration.startParallel() + .addVibrator(3, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + } + + @Test public void testDurationSequential() { assertEquals(26, CombinedVibration.startSequential() .addNext(1, VibrationEffect.createOneShot(10, 10), 10) @@ -178,6 +298,59 @@ public class CombinedVibrationTest { } @Test + public void testDurationSequential_withVibratorSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + // Add each duration and delay + assertEquals(321, CombinedVibration.startSequential() + .addNext(1, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose(), 100) + .addNext(2, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + } + + @Test + public void testDurationSequential_withVibratorNotSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + assertEquals(-1, CombinedVibration.startSequential() + .addNext(1, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose(), 100) + .addNext(2, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + } + + @Test public void testIsHapticFeedbackCandidateMono() { assertTrue(CombinedVibration.createParallel( VibrationEffect.createOneShot(1, 1)).isHapticFeedbackCandidate()); diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java index f5b04ee759a5..8acf2ed87e95 100644 --- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java +++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java @@ -1078,6 +1078,52 @@ public class VibrationEffectTest { } @Test + public void testDuration_withVibratorSupportingPrimitives() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5) + .build(); + + VibrationEffect composition = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose(); + + assertEquals(1, VibrationEffect.createOneShot(1, 1).getDuration()); + assertEquals(10, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration(info)); + assertEquals(115, composition.getDuration(info)); + assertEquals(Long.MAX_VALUE, + VibrationEffect.startComposition() + .repeatEffectIndefinitely(composition) + .compose() + .getDuration(info)); + if (Flags.vendorVibrationEffects()) { + assertEquals(-1, + VibrationEffect.createVendorEffect(createNonEmptyBundle()).getDuration(info)); + } + } + + @Test + public void testDuration_withVibratorNotSupportingPrimitives() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build(); + + VibrationEffect composition = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose(); + + assertEquals(-1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration(info)); + assertEquals(-1, composition.getDuration(info)); + assertEquals(Long.MAX_VALUE, + VibrationEffect.startComposition() + .repeatEffectIndefinitely(composition) + .compose() + .getDuration(info)); + } + + @Test public void testAreVibrationFeaturesSupported_allSegmentsSupported() { VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java index 7dd9e55f8f3e..f9ec5f0b2305 100644 --- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java @@ -24,6 +24,7 @@ import static junit.framework.Assert.assertTrue; import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertThrows; +import android.hardware.vibrator.IVibrator; import android.os.Parcel; import android.os.VibrationEffect; import android.os.VibratorInfo; @@ -114,39 +115,82 @@ public class PrebakedSegmentTest { @Test public void testDuration() { - assertEquals(-1, new PrebakedSegment( - VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) - .getDuration()); - assertEquals(-1, new PrebakedSegment( - VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) - .getDuration()); - assertEquals(-1, new PrebakedSegment( - VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) - .getDuration()); - assertEquals(-1, new PrebakedSegment( - VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK).getDuration()); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TICK).getDuration()); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_THUD).getDuration()); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) .getDuration()); } @Test + public void testDuration_withVibratorSupportingPrimitives_returnsPrimitiveDuration() { + int tickDuration = 5; + int clickDuration = 10; + int thudDuration = 15; + + VibratorInfo vibratorInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, tickDuration) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, clickDuration) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, thudDuration) + .build(); + + assertEquals(5, createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK) + .getDuration(vibratorInfo)); + assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_TICK) + .getDuration(vibratorInfo)); + assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK) + .getDuration(vibratorInfo)); + assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK) + .getDuration(vibratorInfo)); + assertEquals(20, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) + .getDuration(vibratorInfo)); + assertEquals(15, createSegmentWithFallback(VibrationEffect.EFFECT_THUD) + .getDuration(vibratorInfo)); + + // Unknown effects + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_POP) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.RINGTONES[0]) + .getDuration(vibratorInfo)); + } + + @Test + public void testDuration_withVibratorNotSupportingPrimitives_returnsUnknown() { + VibratorInfo vibratorInfo = createVibratorInfoWithSupportedEffects( + VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_POP); + + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TICK) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_THUD) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_POP) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.RINGTONES[0]) + .getDuration(vibratorInfo)); + } + + @Test public void testIsHapticFeedbackCandidate_prebakedConstants_areCandidates() { - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK) .isHapticFeedbackCandidate()); - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK) .isHapticFeedbackCandidate()); - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) .isHapticFeedbackCandidate()); - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_HEAVY_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK) .isHapticFeedbackCandidate()); - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_THUD) .isHapticFeedbackCandidate()); - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_TEXTURE_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK) .isHapticFeedbackCandidate()); } @@ -271,8 +315,7 @@ public class PrebakedSegmentTest { @Test public void testIsHapticFeedbackCandidate_prebakedRingtones_notCandidates() { - assertFalse(new PrebakedSegment( - VibrationEffect.RINGTONES[1], true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertFalse(createSegmentWithFallback(VibrationEffect.RINGTONES[1]) .isHapticFeedbackCandidate()); } diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java index 97f1d5e77ddb..a6d9dc51d7bb 100644 --- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java @@ -201,6 +201,22 @@ public class PrimitiveSegmentTest { } @Test + public void testDuration_withVibratorSupportingPrimitives_returnsVibratorDurationWithDelay() { + VibratorInfo vibratorInfo = createVibratorInfoWithSupportedPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK, /* durationMs= */ 10); + assertEquals(15, new PrimitiveSegment( + VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 5).getDuration(vibratorInfo)); + } + + @Test + public void testDuration_withVibratorNotSupportingPrimitive_returnsUnknown() { + VibratorInfo vibratorInfo = createVibratorInfoWithSupportedPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK); + assertEquals(-1, new PrimitiveSegment( + VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 5).getDuration(vibratorInfo)); + } + + @Test public void testVibrationFeaturesSupport_primitiveSupportedByVibrator() { assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK) .areVibrationFeaturesSupported( @@ -252,9 +268,14 @@ public class PrimitiveSegmentTest { } private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId) { + return createVibratorInfoWithSupportedPrimitive(primitiveId, /* durationMs= */ 10); + } + + private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId, + int durationMs) { return new VibratorInfo.Builder(/* id= */ 1) .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(primitiveId, 10) + .setSupportedPrimitive(primitiveId, durationMs) .build(); } } diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java index bea82931dda7..df874bcb73ca 100644 --- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java @@ -195,7 +195,14 @@ public class RampSegmentTest { @Test public void testDuration() { + VibratorInfo infoWithSupport = + createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true); + VibratorInfo infoWithoutSupport = + createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ false); + assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration()); + assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration(infoWithSupport)); + assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration(infoWithoutSupport)); } @Test diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java index 411074a75e2e..914117c10c87 100644 --- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java @@ -213,7 +213,13 @@ public class StepSegmentTest { @Test public void testDuration() { + VibratorInfo infoWithSupport = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true); + VibratorInfo infoWithoutSupport = + createVibInfoForAmplitude(/* hasAmplitudeControl= */ false); + assertEquals(5, new StepSegment(0, 0, 5).getDuration()); + assertEquals(5, new StepSegment(0, 0, 5).getDuration(infoWithSupport)); + assertEquals(5, new StepSegment(0, 0, 5).getDuration(infoWithoutSupport)); } @Test diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 2e72f0ec90b8..a028e1829ac6 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -572,6 +572,7 @@ applications that come with the platform <permission name="android.permission.READ_BLOCKED_NUMBERS" /> <!-- Permission required for CTS test - PackageManagerTest --> <permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/> + <permission name="android.permission.VERIFICATION_AGENT"/> <!-- Permission required for CTS test CtsInputTestCases --> <permission name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW" /> <!-- Permission required for CTS test - PackageManagerShellCommandInstallTest --> @@ -587,6 +588,9 @@ applications that come with the platform <permission name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" /> <!-- Permission required for CTS test - CtsAppTestCases --> <permission name="android.permission.KILL_UID" /> + <!-- Permission required for CTS test - AdvancedProtectionManagerTest --> + <permission name="android.permission.SET_ADVANCED_PROTECTION_MODE" /> + <permission name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/errorprone/Android.bp b/errorprone/Android.bp index c1d2235e3324..b559a15c3a60 100644 --- a/errorprone/Android.bp +++ b/errorprone/Android.bp @@ -22,6 +22,7 @@ java_library_host { static_libs: [ "annotations", + "jsr305", "framework-annotations-lib", "//external/error_prone:error_prone_core", ], diff --git a/errorprone/OWNERS b/errorprone/OWNERS index bddbdb364683..aa8c126a32e1 100644 --- a/errorprone/OWNERS +++ b/errorprone/OWNERS @@ -1,2 +1 @@ -jsharkey@android.com -jsharkey@google.com +colefaust@google.com diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java index 8dc9579e6b52..6d5e44844a83 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java @@ -29,6 +29,7 @@ import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.fixes.SuggestedFix; import com.google.errorprone.matchers.Description; import com.google.errorprone.util.ASTHelpers; +import com.google.errorprone.util.ErrorProneComment; import com.google.errorprone.util.ErrorProneToken; import com.google.errorprone.util.ErrorProneTokens; import com.sun.source.tree.ClassTree; @@ -37,7 +38,6 @@ import com.sun.source.tree.MethodTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; -import com.sun.tools.javac.parser.Tokens; import java.util.HashMap; import java.util.Map; @@ -66,7 +66,7 @@ public class HideInCommentsChecker extends BugChecker implements final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree, state); final String sourceCode = state.getSourceCode().toString(); for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) { - for (Tokens.Comment comment : token.comments()) { + for (ErrorProneComment comment : token.comments()) { if (!javadocableTrees.containsKey(token.pos())) { continue; } @@ -81,7 +81,7 @@ public class HideInCommentsChecker extends BugChecker implements return NO_MATCH; } - private static Optional<SuggestedFix> generateFix(Tokens.Comment comment) { + private static Optional<SuggestedFix> generateFix(ErrorProneComment comment) { final String text = comment.getText(); if (text.startsWith("/**")) { return Optional.empty(); diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 6d31578ac020..461a5aec27ec 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -128,6 +128,22 @@ public final class Bitmap implements Parcelable { private static final WeakHashMap<Bitmap, Void> sAllBitmaps = new WeakHashMap<>(); /** + * @hide + */ + private static NativeAllocationRegistry getRegistry(boolean malloc, long size) { + final long free = nativeGetNativeFinalizer(); + if (com.android.libcore.readonly.Flags.nativeMetrics()) { + Class cls = Bitmap.class; + return malloc ? NativeAllocationRegistry.createMalloced(cls, free, size) + : NativeAllocationRegistry.createNonmalloced(cls, free, size); + } else { + ClassLoader loader = Bitmap.class.getClassLoader(); + return malloc ? NativeAllocationRegistry.createMalloced(loader, free, size) + : NativeAllocationRegistry.createNonmalloced(loader, free, size); + } + } + + /** * Private constructor that must receive an already allocated native bitmap * int (pointer). */ @@ -151,7 +167,6 @@ public final class Bitmap implements Parcelable { mWidth = width; mHeight = height; mRequestPremultiplied = requestPremultiplied; - mNinePatchChunk = ninePatchChunk; mNinePatchInsets = ninePatchInsets; if (density >= 0) { @@ -159,17 +174,9 @@ public final class Bitmap implements Parcelable { } mNativePtr = nativeBitmap; - final int allocationByteCount = getAllocationByteCount(); - NativeAllocationRegistry registry; - if (fromMalloc) { - registry = NativeAllocationRegistry.createMalloced( - Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount); - } else { - registry = NativeAllocationRegistry.createNonmalloced( - Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount); - } - registry.registerNativeAllocation(this, nativeBitmap); + getRegistry(fromMalloc, allocationByteCount).registerNativeAllocation(this, mNativePtr); + synchronized (Bitmap.class) { sAllBitmaps.put(this, null); } diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java index 671eb6e514c5..ed17fdefcb53 100644 --- a/graphics/java/android/graphics/text/PositionedGlyphs.java +++ b/graphics/java/android/graphics/text/PositionedGlyphs.java @@ -26,6 +26,7 @@ import android.graphics.Typeface; import android.graphics.fonts.Font; import com.android.internal.util.Preconditions; +import com.android.text.flags.Flags; import dalvik.annotation.optimization.CriticalNative; @@ -132,6 +133,9 @@ public final class PositionedGlyphs { @NonNull public Font getFont(@IntRange(from = 0) int index) { Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); + if (Flags.typefaceRedesign()) { + return mFonts.get(nGetFontId(mLayoutPtr, index)); + } return mFonts.get(index); } @@ -245,20 +249,29 @@ public final class PositionedGlyphs { */ public PositionedGlyphs(long layoutPtr, float xOffset, float yOffset) { mLayoutPtr = layoutPtr; - int glyphCount = nGetGlyphCount(layoutPtr); - mFonts = new ArrayList<>(glyphCount); mXOffset = xOffset; mYOffset = yOffset; - long prevPtr = 0; - Font prevFont = null; - for (int i = 0; i < glyphCount; ++i) { - long ptr = nGetFont(layoutPtr, i); - if (prevPtr != ptr) { - prevPtr = ptr; - prevFont = new Font(ptr); + if (Flags.typefaceRedesign()) { + int fontCount = nGetFontCount(layoutPtr); + mFonts = new ArrayList<>(fontCount); + for (int i = 0; i < fontCount; ++i) { + mFonts.add(new Font(nGetFontRef(layoutPtr, i))); + } + } else { + int glyphCount = nGetGlyphCount(layoutPtr); + mFonts = new ArrayList<>(glyphCount); + + long prevPtr = 0; + Font prevFont = null; + for (int i = 0; i < glyphCount; ++i) { + long ptr = nGetFont(layoutPtr, i); + if (prevPtr != ptr) { + prevPtr = ptr; + prevFont = new Font(ptr); + } + mFonts.add(prevFont); } - mFonts.add(prevFont); } NoImagePreloadHolder.REGISTRY.registerNativeAllocation(this, layoutPtr); @@ -290,6 +303,12 @@ public final class PositionedGlyphs { private static native float nGetWeightOverride(long minikinLayout, int i); @CriticalNative private static native float nGetItalicOverride(long minikinLayout, int i); + @CriticalNative + private static native int nGetFontCount(long minikinLayout); + @CriticalNative + private static native long nGetFontRef(long minikinLayout, int fontId); + @CriticalNative + private static native int nGetFontId(long minikinLayout, int glyphIndex); @Override public boolean equals(Object o) { diff --git a/libcore-readonly.aconfig b/libcore-readonly.aconfig new file mode 100644 index 000000000000..3cda92c45190 --- /dev/null +++ b/libcore-readonly.aconfig @@ -0,0 +1,25 @@ +package: "com.android.libcore.readonly" +container: "system" + +# These are the read-only version of the aconfig flags in com.android.libcore +# that will be built with 'force-read-only' mode. +# See b/368409430 - these flags will be removed once the new aconfig API landed. +flag { + namespace: "core_libraries" + name: "post_cleanup_apis" + is_exported: false + description: "This flag includes APIs to add/remove/call callbacks post-cleanup" + bug: "331243037" + # APIs provided by a mainline module can only use a frozen flag. + is_fixed_read_only: true +} + +flag { + namespace: "core_libraries" + name: "native_metrics" + is_exported: false + description: "This flag includes APIs fo maintaining and exposing native allocation metrics" + bug: "331243037" + # APIs provided by a mainline module can only use a frozen flag. + is_fixed_read_only: true +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java index 882a8d035e93..ad194f707cf3 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java @@ -1255,7 +1255,8 @@ class DividerPresenter implements View.OnTouchListener { // Update divider line surface visibility and color. // If a container is fully expanded, the divider line is invisible unless dragging. - final boolean isDividerLineVisible = !mProperties.mIsDraggableExpandType || mIsDragging; + final boolean isDividerLineVisible = mProperties.mDividerWidthPx > 0 + && (!mProperties.mIsDraggableExpandType || mIsDragging); t.setVisibility(mDividerLineSurface, isDividerLineVisible); t.setColor(mDividerLineSurface, colorToFloatArray( Color.valueOf(mProperties.mDividerAttributes.getDividerColor()))); diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt index 96ffa03a1f65..52ce8cb5c0dc 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt @@ -67,6 +67,7 @@ class BubbleStackViewTest { private val context = ApplicationProvider.getApplicationContext<Context>() private lateinit var positioner: BubblePositioner + private lateinit var bubbleLogger: BubbleLogger private lateinit var iconFactory: BubbleIconFactory private lateinit var expandedViewManager: FakeBubbleExpandedViewManager private lateinit var bubbleStackView: BubbleStackView @@ -96,10 +97,11 @@ class BubbleStackViewTest { ) ) positioner = BubblePositioner(context, windowManager) + bubbleLogger = BubbleLogger(UiEventLoggerFake()) bubbleData = BubbleData( context, - BubbleLogger(UiEventLoggerFake()), + bubbleLogger, positioner, BubbleEducationController(context), shellExecutor, @@ -394,6 +396,7 @@ class BubbleStackViewTest { expandedViewManager, bubbleTaskViewFactory, positioner, + bubbleLogger, bubbleStackView, null, iconFactory, diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt index 9fdde128ce41..712cc7c475bc 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt @@ -29,6 +29,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R +import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.protolog.ProtoLog import com.android.internal.statusbar.IStatusBarService import com.android.launcher3.icons.BubbleIconFactory @@ -70,6 +71,7 @@ class BubbleViewInfoTaskTest { private lateinit var bgExecutor: TestExecutor private lateinit var bubbleStackView: BubbleStackView private lateinit var bubblePositioner: BubblePositioner + private lateinit var bubbleLogger: BubbleLogger private lateinit var expandedViewManager: BubbleExpandedViewManager private val bubbleTaskViewFactory = BubbleTaskViewFactory { @@ -103,10 +105,11 @@ class BubbleViewInfoTaskTest { mainExecutor ) bubblePositioner = BubblePositioner(context, windowManager) + bubbleLogger = BubbleLogger(UiEventLoggerFake()) val bubbleData = BubbleData( context, - mock<BubbleLogger>(), + bubbleLogger, bubblePositioner, BubbleEducationController(context), mainExecutor, @@ -138,7 +141,7 @@ class BubbleViewInfoTaskTest { WindowManagerShellWrapper(mainExecutor), mock<UserManager>(), mock<LauncherApps>(), - mock<BubbleLogger>(), + bubbleLogger, mock<TaskStackListenerImpl>(), mock<ShellTaskOrganizer>(), bubblePositioner, @@ -314,6 +317,7 @@ class BubbleViewInfoTaskTest { expandedViewManager, bubbleTaskViewFactory, bubblePositioner, + bubbleLogger, bubbleStackView, null /* layerView */, iconFactory, diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt index 35d459f27534..fa9d2baa78d9 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.bubbles.bar import android.app.ActivityManager import android.content.Context +import android.content.pm.ShortcutInfo import android.graphics.Insets import android.graphics.Rect import android.view.LayoutInflater @@ -27,11 +28,13 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.protolog.ProtoLog import com.android.wm.shell.R import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.BubbleData import com.android.wm.shell.bubbles.BubbleExpandedViewManager +import com.android.wm.shell.bubbles.BubbleLogger import com.android.wm.shell.bubbles.BubblePositioner import com.android.wm.shell.bubbles.BubbleTaskView import com.android.wm.shell.bubbles.BubbleTaskViewFactory @@ -43,11 +46,14 @@ import com.android.wm.shell.shared.handles.RegionSamplingHelper import com.android.wm.shell.taskview.TaskView import com.android.wm.shell.taskview.TaskViewTaskController import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Collections import java.util.concurrent.Executor @@ -70,14 +76,18 @@ class BubbleBarExpandedViewTest { private lateinit var expandedViewManager: BubbleExpandedViewManager private lateinit var positioner: BubblePositioner private lateinit var bubbleTaskView: BubbleTaskView + private lateinit var bubble: Bubble private lateinit var bubbleExpandedView: BubbleBarExpandedView private var testableRegionSamplingHelper: TestableRegionSamplingHelper? = null private var regionSamplingProvider: TestRegionSamplingProvider? = null + private val bubbleLogger = spy(BubbleLogger(UiEventLoggerFake())) + @Before fun setUp() { ProtoLog.REQUIRE_PROTOLOGTOOL = false + ProtoLog.init() mainExecutor = TestExecutor() bgExecutor = TestExecutor() positioner = BubblePositioner(context, windowManager) @@ -106,11 +116,12 @@ class BubbleBarExpandedViewTest { bubbleExpandedView.initialize( expandedViewManager, positioner, + bubbleLogger, false /* isOverflow */, bubbleTaskView, mainExecutor, bgExecutor, - regionSamplingProvider + regionSamplingProvider, ) getInstrumentation().runOnMainSync(Runnable { @@ -118,6 +129,20 @@ class BubbleBarExpandedViewTest { // Helper should be created once attached to window testableRegionSamplingHelper = regionSamplingProvider!!.helper }) + + bubble = Bubble( + "key", + ShortcutInfo.Builder(context, "id").build(), + 100 /* desiredHeight */, + 0 /* desiredHeightResId */, + "title", + 0 /* taskId */, + null /* locus */, + true /* isDismissable */, + directExecutor(), + directExecutor() + ) {} + bubbleExpandedView.update(bubble) } @After @@ -191,6 +216,16 @@ class BubbleBarExpandedViewTest { assertThat(testableRegionSamplingHelper!!.isStopped).isTrue() } + @Test + fun testEventLogging_dismissBubbleViaAppMenu() { + getInstrumentation().runOnMainSync { bubbleExpandedView.handleView.performClick() } + val dismissMenuItem = + bubbleExpandedView.findViewWithTag<View>(BubbleBarMenuView.DISMISS_ACTION_TAG) + assertThat(dismissMenuItem).isNotNull() + getInstrumentation().runOnMainSync { dismissMenuItem.performClick() } + verify(bubbleLogger).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU) + } + private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory { override fun create(): BubbleTaskView { val taskViewTaskController = mock<TaskViewTaskController>() diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml index 35ef2393bb9b..37596182f05b 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml @@ -38,7 +38,7 @@ <Button android:layout_width="94dp" android:layout_height="60dp" - android:id="@+id/maximize_menu_maximize_button" + android:id="@+id/maximize_menu_size_toggle_button" style="?android:attr/buttonBarButtonStyle" android:stateListAnimator="@null" android:importantForAccessibility="yes" @@ -48,7 +48,7 @@ android:alpha="0"/> <TextView - android:id="@+id/maximize_menu_maximize_window_text" + android:id="@+id/maximize_menu_size_toggle_button_text" android:layout_width="94dp" android:layout_height="18dp" android:textSize="11sp" diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index be5a74b7be4d..a0718d9ba148 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerakwessies?\nTik om aan te pas"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tik om die appkieslys oop te maak"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tik om verskeie apps saam te wys"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Keer terug na volskerm van die appkieslys af"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Die appkieslys kan hier gevind word"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Maak rekenaaraansig oop om veelvuldige apps saam oop te maak"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Keer enige tyd terug na volskerm vanaf die appkieslys"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sien en doen meer"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Sleep ’n ander app in vir verdeelde skerm"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dubbeltik buite ’n program om dit te herposisioneer"</string> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index 8c3e3faa2042..f160c70b7505 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"የካሜራ ችግሮች አሉ?\nዳግም ለማበጀት መታ ያድርጉ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"የመተግበሪያ ምናሌውን ለመክፈት መታ ያድርጉ"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"በርካታ መተግበሪያዎችን በአንድ ላይ ለማየት መታ ያድርጉ"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ከመተግበሪያ ምናሌው ወደ ሙሉ ማያ ገፅ ይመለሱ"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"የመተግበሪያ ምናሌው እዚህ መገኘት ይችላል"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"በርካታ መተግበሪያዎችን በአንድ ላይ ለመክፈት ወደ የዴስክቶፕ እይታ ይግቡ"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"በማንኛውም ጊዜ ከመተግበሪያ ምናሌው ላይ ወደ ሙሉ ገጽ እይታ ይመለሱ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ተጨማሪ ይመልከቱ እና ያድርጉ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ለተከፈለ ማያ ገፅ ሌላ መተግበሪያ ይጎትቱ"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ቦታውን ለመቀየር ከመተግበሪያው ውጭ ሁለቴ መታ ያድርጉ"</string> diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index 4a34ce6ad5cf..81706a21f77f 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"هل هناك مشاكل في الكاميرا؟\nانقر لإعادة الضبط."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"انقر لفتح قائمة التطبيق"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"انقر لعرض عدة تطبيقات معًا"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"الرجوع إلى وضع ملء الشاشة من قائمة التطبيق"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"يمكن العثور على قائمة التطبيقات هنا"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"يمكنك الدخول إلى وضع العرض المخصّص للكمبيوتر المكتبي لفتح عدة تطبيقات معًا"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"يمكنك الرجوع إلى وضع ملء الشاشة في أي وقت من قائمة التطبيقات"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"استخدام تطبيقات متعدّدة في وقت واحد"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"اسحب تطبيقًا آخر لاستخدام وضع تقسيم الشاشة."</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"انقر مرّتين خارج تطبيق لتغيير موضعه."</string> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index 94c9ba37c7dc..9fd4941afe84 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"কেমেৰাৰ কোনো সমস্যা হৈছে নেকি?\nপুনৰ খাপ খোৱাবলৈ টিপক"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"এপৰ মেনুখন খুলিবলৈ টিপক"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"একাধিক এপ্ একেলগে দেখুৱাবলৈ টিপক"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"এপৰ মেনুখনৰ পৰা পূৰ্ণ স্ক্ৰীনলৈ উভতি যাওক"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"এপৰ মেনু ইয়াত বিচাৰি পোৱা যাব"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"একেলগে একাধিক এপ্ খুলিবলৈ ডেস্কটপ ভিউলৈ যাওক"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"এপৰ মেনুৰ পৰা যিকোনো সময়তে পূৰ্ণ স্ক্ৰীনলৈ উভতি যাওক"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"চাওক আৰু অধিক কৰক"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"বিভাজিত স্ক্ৰীনৰ বাবে অন্য এটা এপ্ টানি আনি এৰক"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"এপ্টোৰ স্থান সলনি কৰিবলৈ ইয়াৰ বাহিৰত দুবাৰ টিপক"</string> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index 191d074ee714..3ab639730a90 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera problemi var?\nBərpa etmək üçün toxunun"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tətbiq menyusunu açmaq üçün toxunun"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Bir neçə tətbiqi birlikdə göstərmək üçün toxunun"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Tətbiq menyusundan tam ekrana qayıdın"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Tətbiq menyusunu burada tapa bilərsiniz"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Bir neçə tətbiqi birlikdə açmaq üçün masaüstü görünüşə daxil olun"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"İstənilən vaxt tətbiq menyusundan tam ekrana qayıdın"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ardını görün və edin"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Bölünmüş ekran üçün başqa tətbiq sürüşdürün"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tətbiqin yerini dəyişmək üçün kənarına iki dəfə toxunun"</string> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index 852c90e535a1..507625661527 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Imate problema sa kamerom?\nDodirnite da biste ponovo uklopili"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Dodirnite da biste otvorili meni aplikacije"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Dodirnite da biste prikazali više aplikacija zajedno"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Vratite se iz menija aplikacije na prikaz preko celog ekrana"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meni aplikacije možete da pronađete ovde"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Uđite u prikaz za računare da biste istovremeno otvorili više aplikacija"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vratite se na ceo ekran bilo kada iz menija aplikacije"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vidite i uradite više"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite drugu aplikaciju da biste koristili podeljeni ekran"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da biste promenili njenu poziciju"</string> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index 661b6c7aa920..95530c44562e 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Праблемы з камерай?\nНацісніце, каб пераабсталяваць"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Адкрыць меню праграмы"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Паказаць некалькі праграм разам"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Вярнуцца ў поўнаэкранны рэжым з меню праграмы"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Меню праграмы шукайце тут"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Каб адкрыць некалькі праграм адначасова, увайдзіце ў версію для камп’ютараў"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вы можаце вярнуцца ў поўнаэкранны рэжым у любы час з меню праграмы"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Адначасова выконвайце розныя задачы"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Перацягніце іншую праграму, каб выкарыстоўваць падзелены экран"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Двойчы націсніце экран па-за праграмай, каб перамясціць яе"</string> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index d6da2a8703f3..db498c1dfe1a 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблеми с камерата?\nДокоснете за ремонтиране"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Докоснете, за да отворите менюто на приложението"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Докоснете, за да видите няколко приложения заедно"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Връщане към цял екран от менюто на приложението"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Можете да намерите менюто на приложението тук"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Активирайте изгледа за настолни компютри, за да отворите няколко приложения едновременно"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Преминете към цял екран по всяко време от менюто на приложението"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Преглеждайте и правете повече неща"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Преместете друго приложение с плъзгане, за да преминете в режим за разделен екран"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Докоснете два пъти извън дадено приложение, за да промените позицията му"</string> diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml index 62e26b11fa21..4c2025378f04 100644 --- a/libs/WindowManager/Shell/res/values-bn/strings.xml +++ b/libs/WindowManager/Shell/res/values-bn/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ক্যামেরা সংক্রান্ত সমস্যা?\nরিফিট করতে ট্যাপ করুন"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"অ্যাপ মেনু খুলতে ট্যাপ করুন"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"একাধিক অ্যাপ একসাথে দেখতে ট্যাপ করুন"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"অ্যাপ মেনু থেকে ফুল-স্ক্রিন মোডে ফিরে যান"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"অ্যাপ মেনু এখানে খুঁজে পাওয়া যাবে"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"একসাথে একাধিক অ্যাপ খোলার জন্য ডেস্কটপ ভিউতে যান"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"অ্যাপ মেনু থেকে ফুল-স্ক্রিন মোডে যেকোনও সময়ে ফিরে আসুন"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"দেখুন ও আরও অনেক কিছু করুন"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"স্প্লিট স্ক্রিনের ক্ষেত্রে অন্য কোনও অ্যাপ টেনে আনুন"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"কোনও অ্যাপের স্থান পরিবর্তন করতে তার বাইরে ডবল ট্যাপ করুন"</string> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index 15b6058c3196..102a91233627 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s kamerom?\nDodirnite da ponovo namjestite"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Otvaranje menija aplikacije dodirom"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Istovremeni prikaz više aplikacija dodirom"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Povratak na prikaz preko cijelog ekrana putem menija aplikacije"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ovdje možete pronaći meni aplikacije"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Ulazak u prikaz na računaru radi istovremenog otvaranja više aplikacija"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Povratak na prikaz preko cijelog ekrana bilo kada putem menija aplikacije"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Pogledajte i učinite više"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite još jednu aplikaciju za podijeljeni ekran"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da promijenite njen položaj"</string> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index 7cc65a7b075e..3e3fcd05a05b 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tens problemes amb la càmera?\nToca per resoldre\'ls"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toca per obrir el menú de l\'aplicació"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toca per mostrar diverses aplicacions alhora"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Torna a la pantalla completa des del menú de l\'aplicació"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Pots trobar el menú de l\'aplicació aquí"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Accedeix a la visualització per a ordinadors per obrir diverses aplicacions alhora"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Torna a la pantalla completa en qualsevol moment des del menú de l\'aplicació"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta i fes més coses"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrossega una altra aplicació per utilitzar la pantalla dividida"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Fes doble toc fora d\'una aplicació per canviar-ne la posició"</string> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index f8bdcafd8645..0627f54ecf47 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Klepnutím otevřete nabídku aplikace"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Klepnutím zobrazíte několik aplikací najednou"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Návrat na celou obrazovku z nabídky aplikace"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Najdete tu nabídku aplikace"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Pokud chcete otevřít několik aplikací současně, přejděte na zobrazení na počítači"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Na celou obrazovku se můžete kdykoli vrátit z nabídky aplikace"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lepší zobrazení a více možností"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Přetáhnutím druhé aplikace použijete rozdělenou obrazovku"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvojitým klepnutím mimo aplikaci změníte její umístění"</string> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index 1e05069f739a..ed9e5f07bad3 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du problemer med dit kamera?\nTryk for at gendanne det oprindelige format"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tryk for at åbne appmenuen"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tryk for at se flere apps på én gang"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Gå tilbage til fuld skærm via appmenuen"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Appmenuen kan findes her"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Gå til computervenlig visning for at åbne flere apps på én gang"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Gå tilbage til fuld skærm når som helst via appmenuen"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gør mere"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Træk en anden app hertil for at bruge opdelt skærm"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tryk to gange uden for en app for at justere dens placering"</string> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index bccd4ae1d6df..ec1cb03d7108 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Probleme mit der Kamera?\nZum Anpassen tippen."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Zum Öffnen des App-Menüs tippen"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tippen, um mehrere Apps gleichzeitig anzuzeigen"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Über das App-Menü zum Vollbildmodus zurückkehren"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Das App-Menü findest du hier"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Über die Desktop-Ansicht kannst du mehrere Apps gleichzeitig öffnen"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Über das App-Menü kannst du jederzeit zum Vollbildmodus zurückkehren"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Mehr sehen und erledigen"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Für Splitscreen-Modus weitere App hineinziehen"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Außerhalb einer App doppeltippen, um die Position zu ändern"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximieren"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links andocken"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Rechts andocken"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Einstellungen für die Option „Standardmäßig öffnen“"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Festlegen, wie Weblinks für diese App geöffnet werden"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In der App"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In deinem Browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ok"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index 6466215c2865..7a690ce2e188 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Προβλήματα με την κάμερα;\nΠατήστε για επιδιόρθωση."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Πατήστε για άνοιγμα του μενού της εφαρμογής"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Πατήστε για εμφάνιση πολλών εφαρμογών μαζί"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Επιστρέψτε στην πλήρη οθόνη από το μενού της εφαρμογής"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Μπορείτε να βρείτε το μενού εφαρμογών εδώ"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Μεταβείτε στην προβολή για υπολογιστές, για να ανοίξετε πολλές εφαρμογές μαζί"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Επιστρέψτε στην πλήρη οθόνη ανά πάσα στιγμή από το μενού της εφαρμογής"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Δείτε και κάντε περισσότερα"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Σύρετε σε μια άλλη εφαρμογή για διαχωρισμό οθόνης."</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Πατήστε δύο φορές έξω από μια εφαρμογή για να αλλάξετε τη θέση της"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index 018291533e41..afb3f8e1a5ce 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tap to open the app menu"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tap to show multiple apps together"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Return to fullscreen from the app menu"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml index 9d581092748b..aa392519145b 100644 --- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tap to open the app menu"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tap to show multiple apps together"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Return to fullscreen from the app menu"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen anytime from the app menu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index 018291533e41..afb3f8e1a5ce 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tap to open the app menu"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tap to show multiple apps together"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Return to fullscreen from the app menu"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index 018291533e41..afb3f8e1a5ce 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tap to open the app menu"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tap to show multiple apps together"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Return to fullscreen from the app menu"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index 630d8067f381..5244a61883d0 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Tienes problemas con la cámara?\nPresiona para reajustarla"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Presiona para abrir el menú de la app"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Presiona para mostrar varias apps juntas"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Regresa a pantalla completa desde el menú de la app"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"El menú de la app se encuentra aquí"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entra a la vista de escritorio para abrir varias apps a la vez"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Regresa a pantalla completa en cualquier momento desde el menú de la app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Aprovecha más"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra otra app para el modo de pantalla dividida"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Presiona dos veces fuera de una app para cambiar su ubicación"</string> diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml index 9f6b2e65c27c..a673351f8316 100644 --- a/libs/WindowManager/Shell/res/values-es/strings.xml +++ b/libs/WindowManager/Shell/res/values-es/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Problemas con la cámara?\nToca para reajustar"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toca para abrir el menú de aplicaciones"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toca para mostrar varias aplicaciones a la vez"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Vuelve a pantalla completa desde el menú de aplicaciones"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"El menú de la aplicación se encuentra aquí"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entra en la vista para ordenador si quieres abrir varias aplicaciones a la vez"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vuelve a la pantalla completa en cualquier momento desde el menú de aplicaciones"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta más información y haz más"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra otra aplicación para activar la pantalla dividida"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toca dos veces fuera de una aplicación para cambiarla de posición"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Acoplar a la izquierda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Acoplar a la derecha"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir con los ajustes predeterminados"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Elige cómo quieres abrir los enlaces web de esta aplicación"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"En la aplicación"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"En el navegador"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index 25c068b1fa43..686385c4bdb8 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kas teil on kaameraprobleeme?\nPuudutage ümberpaigutamiseks."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Puudutage rakenduse menüü avamiseks"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Puudutage mitme rakenduse koos kuvamiseks"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Minge rakenduse menüüst tagasi täisekraanile"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Rakenduse menüü leiate siit"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Mitme rakenduse koos avamiseks avage arvutivaade"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Saate rakenduse menüüst igal ajal täisekraanile naasta"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vaadake ja tehke rohkem"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Lohistage muusse rakendusse, et jagatud ekraanikuva kasutada"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Topeltpuudutage rakendusest väljaspool, et selle asendit muuta"</string> diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml index ba4cbc88120b..1f0e54cedd58 100644 --- a/libs/WindowManager/Shell/res/values-eu/strings.xml +++ b/libs/WindowManager/Shell/res/values-eu/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Arazoak dauzkazu kamerarekin?\nBerriro doitzeko, sakatu hau."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Sakatu hau aplikazioen menua irekitzeko"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Sakatu hau aplikazio bat baino gehiago aldi berean erakusteko"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Itzuli pantaila osora aplikazioen menutik"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Aplikazioaren menua dago hemen"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Sartu ordenagailuetarako ikuspegian aplikazio bat baino gehiago batera irekitzeko"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Pantaila osoko modura itzultzeko, erabili aplikazioaren menua"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ikusi eta egin gauza gehiago"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Pantaila zatitua ikusteko, arrastatu beste aplikazio bat"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Aplikazioaren posizioa aldatzeko, sakatu birritan haren kanpoaldea"</string> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index 95bad9cce976..ad39b28eed1e 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه تکضرب بزنید"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن تکضرب بزنید"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن تکضرب بزنید."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"برای باز کردن منو برنامه، تکضرب بزنید"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"برای نمایش چند برنامه با هم، تکضرب بزنید"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"از منو برنامه به تمامصفحه برگردید"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"منو برنامه را میتوانید اینجا ببینید"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"برای باز کردن همزمان چند برنامه، وارد نمای ویژه رایانه شوید"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"هروقت خواستید از منو برنامه به حالت تمامصفحه برگردید"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"از چندین برنامه بهطور همزمان استفاده کنید"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"برای حالت صفحهٔ دونیمه، در برنامهای دیگر بکشید"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"برای جابهجا کردن برنامه، بیرون از آن دو تکضرب بزنید"</string> diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml index 9c841afc9983..2f0edd33226b 100644 --- a/libs/WindowManager/Shell/res/values-fi/strings.xml +++ b/libs/WindowManager/Shell/res/values-fi/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Onko kameran kanssa ongelmia?\nKorjaa napauttamalla"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Avaa sovellusvalikko napauttamalla"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Näytä useita sovelluksia yhdessä napauttamalla"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Palaa koko näytön tilaan sovellusvalikosta"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Sovellusvalikko löytyy täältä"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Siirry työpöytänäkymään, niin voit avata useita sovelluksia kerralla"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Voit palata koko näytön tilaan milloin tahansa sovellusvalikosta"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Näe ja tee enemmän"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Käytä jaettua näyttöä vetämällä tähän toinen sovellus"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Kaksoisnapauta sovelluksen ulkopuolella, jos haluat siirtää sitä"</string> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index c163165a8296..245396156aad 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toucher ici pour ouvrir le menu de l\'appli"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toucher ici pour afficher plusieurs applis ensemble"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Revenir au mode Plein écran à partir du menu de l\'appli"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Le menu de l\'appli se trouve ici"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Accéder à l\'affichage sur un ordinateur de bureau pour ouvrir plusieurs applis simultanément"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revenir au mode Plein écran à tout moment à partir du menu de l\'appli"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et en faire plus"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Touchez deux fois à côté d\'une appli pour la repositionner"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Épingler à gauche"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Épingler à droite"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ouvrir les paramètres par défaut"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choisissez comment ouvrir les liens Web pour cette appli"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Dans l\'appli"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Dans votre navigateur"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml index b2a6f16733b3..aee234565ad0 100644 --- a/libs/WindowManager/Shell/res/values-fr/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo ?\nAppuyez pour réajuster"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Appuyer pour ouvrir le menu de l\'appli"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Appuyer pour afficher plusieurs applis simultanément"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Revenir en plein écran depuis le menu de l\'appli"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Le menu de l\'application se trouve ici"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Passer à l\'affichage sur ordinateur pour ouvrir plusieurs applications simultanément"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revenir en plein écran à tout moment depuis le menu de l\'application"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et interagir plus"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Appuyez deux fois en dehors d\'une appli pour la repositionner"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ancrer à gauche"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ancrer à droite"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ouvrir les paramètres par défaut"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choisir comment ouvrir les liens Web pour cette appli"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Dans l\'application"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Dans votre navigateur"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index 0cc559f79dfa..b61588ac24ed 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tes problemas coa cámara?\nToca para reaxustala"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toca para abrir o menú da aplicación"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toca para mostrar varias aplicacións xuntas"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Volve á pantalla completa desde o menú da aplicación"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Aquí podes ver o menú da aplicación"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Vai á vista para ordenadores se queres abrir varias aplicacións á vez"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volve á pantalla completa en calquera momento desde o menú da aplicación"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ver e facer máis"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra outra aplicación para usar a pantalla dividida"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toca dúas veces fóra da aplicación para cambiala de posición"</string> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index 460f8709ccc0..fd4f2baeb871 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"કૅમેરામાં સમસ્યાઓ છે?\nફરીથી ફિટ કરવા માટે ટૅપ કરો"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ઍપ મેનૂ ખોલવા માટે ટૅપ કરો"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"એકથી વધુ ઍપ એકસાથે બતાવવા માટે ટૅપ કરો"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ઍપ મેનૂમાંથી પૂર્ણસ્ક્રીન પર પાછા ફરો"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ઍપ મેનૂ અહીં જોવા મળી શકે છે"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"એકથી વધુ ઍપ એકસાથે ખોલવા માટે ડેસ્કટૉપ વ્યૂ દાખલ કરો"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ઍપ મેનૂમાંથી કોઈપણ સમયે પૂર્ણ સ્ક્રીન પર પાછા ફરો"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"જુઓ અને બીજું ઘણું કરો"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"વિભાજિત સ્ક્રીન માટે કોઈ અન્ય ઍપમાં ખેંચો"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"કોઈ ઍપની જગ્યા બદલવા માટે, તેની બહાર બે વાર ટૅપ કરો"</string> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index 17ceca187b39..d2cd23df8296 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्या कैमरे से जुड़ी कोई समस्या है?\nफिर से फ़िट करने के लिए टैप करें"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ऐप्लिकेशन मेन्यू खोलने के लिए टैप करें"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"कई ऐप्लिकेशन एक साथ दिखाने के लिए टैप करें"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ऐप्लिकेशन मेन्यू से फ़ुलस्क्रीन मोड पर वापस जाएं"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ऐप्लिकेशन मेन्यू यहां पाया जा सकता है"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"एक साथ कई ऐप्लिकेशन खोलने के लिए, डेस्कटॉप व्यू में जाएं"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ऐप्लिकेशन मेन्यू से फ़ुल स्क्रीन मोड पर किसी भी समय वापस जाएं"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पूरी जानकारी लेकर, बेहतर तरीके से काम करें"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रीन का इस्तेमाल करने के लिए, किसी अन्य ऐप्लिकेशन को खींचें और छोड़ें"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"किसी ऐप्लिकेशन की जगह बदलने के लिए, उसके बाहर दो बार टैप करें"</string> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index 20fa5468e296..80949b4c8edd 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s fotoaparatom?\nDodirnite za popravak"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Dodirnite za otvaranje izbornika aplikacije"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Dodirnite za prikaz više aplikacija zajedno"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Vratite se na cijeli zaslon iz izbornika aplikacije"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Izbornik aplikacije možete pronaći ovdje"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Otvorite prikaz na računalu da biste otvorili više aplikacija zajedno"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vratite se na cijeli zaslon bilo kad iz izbornika aplikacije"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Gledajte i učinite više"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Povucite drugu aplikaciju unutra da biste podijelili zaslon"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da biste je premjestili"</string> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index 78cec15cc44a..cebf5857db34 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerával kapcsolatos problémába ütközött?\nKoppintson a megoldáshoz."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Koppintson az alkalmazásmenü megnyitásához"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Koppintson több alkalmazás együttes megjelenítéséhez"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"A teljes képernyőre az alkalmazásmenüben térhet vissza"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Az alkalmazásmenü itt található"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Asztali nézetbe lépve több alkalmazást nyithat meg egyidejűleg"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Az alkalmazásmenüből bármikor visszatérhet a teljes képernyőre"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Több mindent láthat és tehet"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Húzzon ide egy másik alkalmazást az osztott képernyő használatához"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Koppintson duplán az alkalmazáson kívül az áthelyezéséhez"</string> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index 1461322da16e..63a9d6d92134 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Տեսախցիկի հետ կապված խնդիրնե՞ր կան։\nՀպեք՝ վերակարգավորելու համար։"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Հպեք՝ հավելվածի ընտրացանկը բացելու համար"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Հպեք՝ էկրանին մի քանի հավելված միասին դիտելու համար"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Հավելվածի ընտրացանկից վերադառնալ լիաէկրան ռեժիմ"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Հավելվածի ընտրացանկն այստեղ է"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Անցեք համակարգչային տարբերակին՝ միաժամանակ մի քանի հավելված բացելու համար"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ցանկացած ժամանակ հավելվածի ընտրացանկից վերադարձեք լիաէկրան ռեժիմ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Միաժամանակ կատարեք մի քանի առաջադրանք"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Քաշեք մյուս հավելվածի մեջ՝ էկրանի տրոհումն օգտագործելու համար"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Կրկնակի հպեք հավելվածի կողքին՝ այն տեղափոխելու համար"</string> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index 23626e49b8db..a06d01cc524a 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Masalah kamera?\nKetuk untuk memperbaiki"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Ketuk untuk membuka menu aplikasi"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Ketuk untuk menampilkan beberapa aplikasi secara bersamaan"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Kembali ke layar penuh dari menu aplikasi"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menu aplikasi dapat ditemukan di sini"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Buka tampilan desktop untuk membuka beberapa aplikasi secara bersamaan"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kembali ke layar penuh kapan saja dari menu aplikasi"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih banyak hal"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Tarik aplikasi lain untuk menggunakan layar terpisah"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketuk dua kali di luar aplikasi untuk mengubah posisinya"</string> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index 6ae4c7c60bc7..a20f4604ff90 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Myndavélavesen?\nÝttu til að breyta stærð"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Ýttu til að opna forritavalmyndina"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Ýttu til að sjá mörg forrit saman"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Opnaðu allan skjáinn aftur á forritavalmyndinni"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Hér finnurðu forritavalmyndina"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Veldu tölvuútgáfu til að opna mörg forrit samtímis"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Þú getur skipt aftur í allan skjáinn hvenær sem er af forritavalmyndinni"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sjáðu og gerðu meira"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dragðu annað forrit inn til að nota skjáskiptingu"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ýttu tvisvar utan við forrit til að færa það"</string> diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml index b56424594f29..39fd6ba326a6 100644 --- a/libs/WindowManager/Shell/res/values-it/strings.xml +++ b/libs/WindowManager/Shell/res/values-it/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi con la fotocamera?\nTocca per risolverli"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tocca per aprire il menu dell\'app"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tocca per mostrare più app insieme"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Torna allo schermo intero dal menu dell\'app"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Il menu dell\'app si trova qui"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entra nella visualizzazione desktop per aprire più app contemporaneamente"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Torna allo schermo intero in qualsiasi momento dal menu dell\'app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Visualizza più contenuti e fai di più"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trascina in un\'altra app per usare lo schermo diviso"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tocca due volte fuori da un\'app per riposizionarla"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ingrandisci"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Aggancia a sinistra"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Aggancia a destra"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Apri in base alle impostazioni predefinite"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Scegli come aprire i link web per questa app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"All\'interno dell\'app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Nel browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ok"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml index 41765e3c56d5..0586d1f8cd16 100644 --- a/libs/WindowManager/Shell/res/values-iw/strings.xml +++ b/libs/WindowManager/Shell/res/values-iw/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"בעיות במצלמה?\nאפשר להקיש כדי לבצע התאמה מחדש"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר להקיש כדי לחזור לגרסה הקודמת"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר להקיש כדי לסגור."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"צריך להקיש כדי לפתוח את תפריט האפליקציה"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"אפשר להקיש כדי להציג כמה אפליקציות יחד"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"חזרה למסך מלא מתפריט האפליקציה"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"תפריט האפליקציה נמצא כאן"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"כדי לפתוח כמה אפליקציות יחד, צריך לעבור למצב תצוגה למחשב"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"אפשר לחזור למסך מלא בכל שלב מתפריט האפליקציה"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"רוצה לראות ולעשות יותר?"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"צריך לגרור אפליקציה אחרת כדי להשתמש במסך המפוצל"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"צריך להקיש הקשה כפולה מחוץ לאפליקציה כדי למקם אותה מחדש"</string> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index c7dfd457950a..8aa8269af8a5 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"カメラに関する問題の場合は、\nタップすると修正できます"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"タップするとアプリメニューが開きます"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"タップすると複数のアプリが同時に表示されます"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"アプリメニューから全画面表示に戻ります"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"アプリメニューはここにあります"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"デスクトップ ビューに切り替えて複数のアプリを同時に開けます"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"アプリメニューからいつでも全画面表示に戻れます"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"表示を拡大して機能を強化"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"分割画面にするにはもう 1 つのアプリをドラッグしてください"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"位置を変えるにはアプリの外側をダブルタップしてください"</string> diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml index 4986c2d63761..6672599d2763 100644 --- a/libs/WindowManager/Shell/res/values-ka/strings.xml +++ b/libs/WindowManager/Shell/res/values-ka/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"კამერად პრობლემები აქვს?\nშეეხეთ გამოსასწორებლად"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"შეეხეთ აპის მენიუს გასახსნელად"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"შეეხეთ რამდენიმე აპის ერთად საჩვენებლად"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"სრულეკრანიან რეჟიმზე დაბრუნდით აპის მენიუდან"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"აპის მენიუ შეგიძლიათ იხილოთ აქ"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"რამდენიმე აპის ერთდროულად გასახსნელად შედით დესკტოპის ხედში"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"სრულ ეკრანზე ნებისმიერ დროს შეგიძლიათ დაბრუნდეთ აპის მენიუდან"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"მეტის ნახვა და გაკეთება"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ეკრანის გასაყოფად ჩავლებით გადაიტანეთ სხვა აპში"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ორმაგად შეეხეთ აპის გარშემო სივრცეს, რათა ის სხვაგან გადაიტანოთ"</string> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index 7c49ae5abf4e..56ae4416192a 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада қателер шықты ма?\nЖөндеу үшін түртіңіз."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Қолданба мәзірін ашу үшін түртіңіз"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Бірнеше қолданбаны қатар көрсету үшін түртіңіз"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Қолданба мәзірінен толық экран режиміне қайту"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Қолданба мәзірін осы жерден табуға болады."</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Бірнеше қолданбаны бірге ашу үшін компьютерлік нұсқаны қосыңыз."</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Қолданба мәзірінен кез келген уақытта толық экранға оралыңыз."</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Қосымша ақпаратты қарап, әрекеттер жасау"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлу үшін басқа қолданбаға өтіңіз."</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Қолданбаның орнын өзгерту үшін одан тыс жерді екі рет түртіңіз."</string> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index c6af12528d93..460b8678c2c9 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"មានបញ្ហាពាក់ព័ន្ធនឹងកាមេរ៉ាឬ?\nចុចដើម្បីដោះស្រាយ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបានដោះស្រាយបញ្ហានេះទេឬ?\nចុចដើម្បីត្រឡប់"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមានបញ្ហាពាក់ព័ន្ធនឹងកាមេរ៉ាទេឬ? ចុចដើម្បីច្រានចោល។"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ចុចដើម្បីបើកម៉ឺនុយកម្មវិធី"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ចុចដើម្បីបង្ហាញកម្មវិធីច្រើនរួមគ្នា"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ត្រឡប់ទៅអេក្រង់ពេញវិញពីម៉ឺនុយកម្មវិធី"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"អាចរកឃើញម៉ឺនុយកម្មវិធីនៅទីនេះ"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ចូលទិដ្ឋភាពលើកុំព្យូទ័រ ដើម្បីបើកកម្មវិធីច្រើនជាមួយគ្នា"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ត្រឡប់ទៅអេក្រង់ពេញវិញនៅពេលណាក៏បានពីម៉ឺនុយកម្មវិធី"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"មើលឃើញ និងធ្វើបានកាន់តែច្រើន"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"អូសកម្មវិធីមួយទៀតចូល ដើម្បីប្រើមុខងារបំបែកអេក្រង់"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ចុចពីរដងនៅក្រៅកម្មវិធី ដើម្បីប្ដូរទីតាំងកម្មវិធីនោះ"</string> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index 498ad0f04a5e..2e2be46c21f6 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿವೆಯೇ?\nಮರುಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ಆ್ಯಪ್ ಮೆನುವನ್ನು ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ಅನೇಕ ಆ್ಯಪ್ಗಳನ್ನು ಒಟ್ಟಿಗೆ ತೋರಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ಆ್ಯಪ್ ಮೆನುವಿನಿಂದ ಫುಲ್ಸ್ಕ್ರೀನ್ಗೆ ಹಿಂತಿರುಗಿ"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ಆ್ಯಪ್ ಮೆನುವನ್ನು ಇಲ್ಲಿ ಕಾಣಬಹುದು"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ಹಲವು ಆ್ಯಪ್ಗಳನ್ನು ಒಟ್ಟಿಗೆ ತೆರೆಯಲು ಡೆಸ್ಕ್ಟಾಪ್ ವೀಕ್ಷಣೆಯನ್ನು ನಮೂದಿಸಿ"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ಆ್ಯಪ್ ಮೆನುವಿನಿಂದ ಯಾವಾಗ ಬೇಕಾದರೂ ಫುಲ್ಸ್ಕ್ರೀನ್ಗೆ ಹಿಂತಿರುಗಿ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ನೋಡಿ ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ಮಾಡಿ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ಗಾಗಿ ಮತ್ತೊಂದು ಆ್ಯಪ್ನಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ಆ್ಯಪ್ ಒಂದರ ಸ್ಥಾನವನ್ನು ಬದಲಾಯಿಸಲು ಅದರ ಹೊರಗೆ ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ"</string> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index a92d3cb21b76..4bcc76dd0259 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"카메라 문제가 있나요?\n해결하려면 탭하세요."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"탭하여 앱 메뉴 열기"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"탭하여 여러 앱을 함께 표시하기"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"앱 메뉴에서 전체 화면으로 돌아가기"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"앱 메뉴는 여기에서 찾을 수 있습니다."</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"데스크톱 뷰를 실행하여 여러 앱을 함께 열 수 있습니다."</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"언제든지 앱 메뉴에서 전체 화면으로 돌아갈 수 있습니다."</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"더 많은 정보를 보고 더 많은 작업을 처리하세요"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"화면 분할을 사용하려면 다른 앱을 드래그해 가져옵니다."</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"앱 위치를 조정하려면 앱 외부를 두 번 탭합니다."</string> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index b01659d95407..6ae51a4ab770 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада маселелер келип чыктыбы?\nОңдоо үчүн таптаңыз"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Колдонмонун менюсун ачуу үчүн таптап коюңуз"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Бир нече колдонмону чогуу көрүү үчүн таптап коюңуз"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Колдонмонун менюсунан толук экранга кайтыңыз"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Колдонмонун менюсун ушул жерден таба аласыз"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Бир убакта бир нече колдонмону ачуу үчүн компьютердик версияга өтүңүз"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Каалаган убакта колдонмонун менюсунан толук экранга кайта аласыз"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Көрүп, көбүрөөк нерселерди жасаңыз"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлүү үчүн башка колдонмону сүйрөңүз"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Колдонмону жылдыруу үчүн сырт жагын эки жолу таптаңыз"</string> diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml index f3e7169e1431..f8a09da308b9 100644 --- a/libs/WindowManager/Shell/res/values-lo/strings.xml +++ b/libs/WindowManager/Shell/res/values-lo/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ?\nແຕະເພື່ອປັບໃໝ່"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອປິດໄວ້."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ແຕະເພື່ອເປີດເມນູແອັບ"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ແຕະເພື່ອສະແດງແອັບຫຼາຍລາຍການພ້ອມກັນ"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ກັບຄືນໄປຫາໂໝດເຕັມຈໍຈາກເມນູແອັບ"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ສາມາດເບິ່ງເມນູແອັບໄດ້ບ່ອນນີ້"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ເຂົ້າສູ່ມຸມມອງເດັສທັອບເພື່ອເປີດຫຼາຍແອັບພ້ອມກັນ"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ກັບຄືນໄປຫາໂໝດເຕັມຈໍໄດ້ທຸກເວລາຈາກເມນູແອັບ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ເບິ່ງ ແລະ ເຮັດຫຼາຍຂຶ້ນ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ລາກໄປໄວ້ໃນແອັບອື່ນເພື່ອແບ່ງໜ້າຈໍ"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ແຕະສອງເທື່ອໃສ່ນອກແອັບໃດໜຶ່ງເພື່ອຈັດຕຳແໜ່ງຂອງມັນຄືນໃໝ່"</string> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index 719cf60d18e2..857e90e6d340 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Iškilo problemų dėl kameros?\nPalieskite, kad pritaikytumėte iš naujo"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Palieskite, kad atidarytumėte programos meniu"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Palieskite, kad būtų rodomos kelios programos kartu"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Grįžkite į viso ekrano režimą iš programos meniu"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Programos meniu rasite čia"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Įjunkite rodinio versiją staliniams kompiuteriams, kad vienu metu atidarytumėte kelias programas"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Bet kada iš programos meniu grįžkite į viso ekrano režimą"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daugiau turinio ir funkcijų"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Vilkite kitoje programoje, kad galėtumėte naudoti išskaidyto ekrano režimą"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dukart palieskite už programos ribų, kad pakeistumėte jos poziciją"</string> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index 1649a2e54c12..e56363e06c46 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Vai ir problēmas ar kameru?\nPieskarieties, lai tās novērstu."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Lai atvērtu lietotnes izvēlni, pieskarieties."</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Lai parādītu vairākas lietotnes kopā, pieskarieties."</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Varat atgriezties pilnekrāna režīmā no lietotnes izvēlnes."</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Šeit ir pieejama lietotņu izvēlne"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Lai atvērtu vairākas lietotnes vienlaikus, pārejiet uz skatu datorā"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"No lietotnes izvēlnes varat jebkurā brīdī atgriezties pilnekrāna režīmā."</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Uzziniet un paveiciet vairāk"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Lai izmantotu sadalītu ekrānu, ievelciet vēl vienu lietotni"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Lai pārvietotu lietotni, veiciet dubultskārienu ārpus lietotnes"</string> diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml index 4922d042c6ac..1bf7a28aa410 100644 --- a/libs/WindowManager/Shell/res/values-mk/strings.xml +++ b/libs/WindowManager/Shell/res/values-mk/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми со камерата?\nДопрете за да се совпадне повторно"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Допрете за да го отворите менито со апликации"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Допрете за да се прикажат повеќе апликации заедно"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Вратете се на цел екран од менито со апликации"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Менито со апликации може да го најдете овде"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Влезете во приказот на компјутер за да отворите повеќе апликации заедно"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вратете се на цел екран од менито со апликации кога сакате"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Погледнете и направете повеќе"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Повлечете друга апликација за поделен екран"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Допрете двапати надвор од некоја апликација за да ја преместите"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Максимизирај"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Фотографирај лево"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Фотографирај десно"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Отвори според стандардните поставки"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Изберете како да се отвораат линковите за апликацијава"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Во апликацијата"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Во прелистувачот"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Во ред"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index 4df5d6f0fc8e..3401f6d6b54a 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ക്യാമറ പ്രശ്നങ്ങളുണ്ടോ?\nശരിയാക്കാൻ ടാപ്പ് ചെയ്യുക"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ആപ്പ് മെനു തുറക്കാൻ ടാപ്പ് ചെയ്യുക"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ഒന്നിലധികം ആപ്പുകൾ ഒരുമിച്ച് കാണിക്കാൻ ടാപ്പ് ചെയ്യുക"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ആപ്പ് മെനുവിൽ നിന്ന് പൂർണ്ണസ്ക്രീനിലേക്ക് മടങ്ങുക"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ആപ്പ് മെനു ഇവിടെ കണ്ടെത്താനാകും"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ഒന്നിലധികം ആപ്പുകൾ ഒരുമിച്ച് തുറക്കാൻ ഡെസ്ക്ടോപ്പ് വ്യൂവിൽ പ്രവേശിക്കുക"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ആപ്പ് മെനുവിൽ നിന്ന് ഏതുസമയത്തും പൂർണ്ണ സ്ക്രീനിലേക്ക് മടങ്ങുക"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"കൂടുതൽ കാണുക, ചെയ്യുക"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"സ്ക്രീൻ വിഭജന മോഡിന്, മറ്റൊരു ആപ്പ് വലിച്ചിടുക"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ആപ്പിന്റെ സ്ഥാനം മാറ്റാൻ അതിന് പുറത്ത് ഡബിൾ ടാപ്പ് ചെയ്യുക"</string> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index 70f6a0806468..87708d0a0cc2 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерын асуудал гарсан уу?\nДахин тааруулахын тулд товшино уу"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Аппын цэсийг нээхийн тулд товшино уу"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Олон аппыг хамтад нь харуулахын тулд товшино уу"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Аппын цэсээс бүтэн дэлгэц рүү буцна уу"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Аппын цэсийг эндээс олох боломжтой"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Олон аппыг хамтад нь нээхийн тулд дэлгэц дээр харагдах байдалд орно уу"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Аппын цэсээс бүтэн дэлгэц рүү хүссэн үедээ буцна уу"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Харж илүү ихийг хий"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Дэлгэц хуваах горимд ашиглахын тулд өөр аппыг чирнэ үү"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Аппыг дахин байрлуулахын тулд гадна талд нь хоёр товшино"</string> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index e2844861e31a..1ea41e557c4f 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"कॅमेराशी संबंधित काही समस्या आहेत का?\nपुन्हा फिट करण्यासाठी टॅप करा"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्यासाठी टॅप करा."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"अॅप मेनू उघडण्यासाठी टॅप करा"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"एकाहून अधिक ॲप्स एकत्र दाखवण्यासाठी टॅप करा"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ॲप मेनूमधून फुलस्क्रीनवर परत या"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ॲप मेनू इथे आढळू शकतो"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"एकाहून अधिक ॲप्स एकत्र उघडण्यासाठी डेस्कटॉप दृश्यात एंटर करा"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ॲप मेनूमधून कधीही फुल स्क्रीनवर परत या"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पहा आणि आणखी बरेच काही करा"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रीन वापरण्यासाठी दुसरे ॲप ड्रॅग करा"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ॲपची स्थिती पुन्हा बदलण्यासाठी, त्याच्या बाहेर दोनदा टॅप करा"</string> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index 59032fb91d92..ca248e133eb8 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Isu kamera?\nKetik untuk memuatkan semula"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Ketik untuk membuka menu apl"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Ketik untuk memaparkan berbilang apl serentak"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Kembali kepada skrin penuh daripada menu apl"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menu apl boleh ditemukan di sini"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Masuki paparan desktop untuk membuka berbilang apl serentak"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kembali kepada skrin penuh pada bila-bila masa daripada menu apl"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Seret masuk apl lain untuk menggunakan skrin pisah"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketik dua kali di luar apl untuk menempatkan semula apl itu"</string> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index 7b952c1f3fa8..3c4325bfc5b8 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ကင်မရာပြဿနာလား။\nပြင်ဆင်ရန် တို့ပါ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"အက်ပ်မီနူးကိုဖွင့်ရန် တို့ပါ"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"အက်ပ်များစွာကို အတူတကွပြရန် တို့ပါ"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"အက်ပ်မီနူးမှ ဖန်သားပြင်အပြည့်သို့ ပြန်သွားပါ"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"အက်ပ်မီနူးကို ဤနေရာတွင် တွေ့နိုင်သည်"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"အက်ပ်များစွာကို အတူတကွဖွင့်ရန်အတွက် ဒက်စ်တော့မြင်ကွင်းသို့ ဝင်ရောက်နိုင်သည်"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"အက်ပ်မီနူးမှ ဖန်သားပြင်အပြည့်သို့ အချိန်မရွေး ပြန်သွားနိုင်သည်"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ကြည့်ပြီး ပိုမိုလုပ်ဆောင်ပါ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းအတွက် အက်ပ်နောက်တစ်ခုကို ဖိဆွဲပါ"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"နေရာပြန်ချရန် အက်ပ်အပြင်ဘက်ကို နှစ်ချက်တို့ပါ"</string> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 8733a7f7f7ef..4096bbf69fe7 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Trykk for å åpne appmenyen"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Trykk for å vise flere apper sammen"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Gå tilbake til fullskjerm fra appmenyen"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Her finner du appmenyen"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Gå til skrivebordsvisningen for å åpne flere apper samtidig"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Du kan gå tilbake til fullskjermmodusen når som helst fra appmenyen"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gjør mer"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dra inn en annen app for å bruke delt skjerm"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dobbelttrykk utenfor en app for å flytte den"</string> diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index cdd2a1f80d1d..2fc5e0902efa 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्यामेरासम्बन्धी समस्या देखियो?\nसमस्या हल गर्न ट्याप गर्नुहोस्"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"एपको मेनु खोल्न ट्याप गर्नुहोस्"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"एकभन्दा बढी एपहरू सँगै देखाउन ट्याप गर्नुहोस्"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"एपको मेनुबाट फुल स्क्रिनमा फर्कनुहोस्"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"एपको मेनु यहाँ भेट्टाउन सकिन्छ"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"एकभन्दा बढी एपहरू सँगै देखाउन डेस्कटप भ्यू हाल्नुहोस्"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"जुनसुकै बेला एपको मेनुबाट फुल स्क्रिनमा फर्कनुहोस्"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"थप कुरा हेर्नुहोस् र गर्नुहोस्"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रिन मोड प्रयोग गर्न अर्को एप ड्रयाग एन्ड ड्रप गर्नुहोस्"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"तपाईं जुन एपको स्थिति मिलाउन चाहनुहुन्छ सोही एपको बाहिर डबल ट्याप गर्नुहोस्"</string> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index d8a285649952..65fd8ea44d2e 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Cameraproblemen?\nTik om opnieuw passend te maken."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tik om het app-menu te openen"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tik om meerdere apps tegelijk te tonen"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Terug naar volledig scherm vanuit het app-menu"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Het app-menu vind je hier"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Ga naar de desktopweergave om meerdere apps tegelijk te openen"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ga wanneer je wilt terug naar volledig scherm vanuit het app-menu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zie en doe meer"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Sleep een andere app hier naartoe om het scherm te splitsen"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dubbeltik naast een app om deze opnieuw te positioneren"</string> diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml index baf009e8f28c..1f96daad93b9 100644 --- a/libs/WindowManager/Shell/res/values-or/strings.xml +++ b/libs/WindowManager/Shell/res/values-or/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"କ୍ୟାମେରାରେ ସମସ୍ୟା ଅଛି?\nପୁଣି ଫିଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ଆପ ମେନୁ ଖୋଲିବାକୁ ଟାପ କରନ୍ତୁ"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ଏକାଠି ଏକାଧିକ ଆପ୍ସ ଦେଖାଇବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ଆପ ମେନୁରୁ ପୂର୍ଣ୍ଣସ୍କ୍ରିନକୁ ଫେରନ୍ତୁ"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ଆପ ମେନୁ ଏଠାରେ ମିଳିପାରିବ"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ଏକାଠି ଏକାଧିକ ଆପ୍ସ ଖୋଲିବାକୁ ଡେସ୍କଟପ ଭ୍ୟୁରେ ପ୍ରବେଶ କରନ୍ତୁ"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ଆପ ମେନୁରୁ ଯେ କୌଣସି ସମୟରେ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ଫେରନ୍ତୁ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ଦେଖନ୍ତୁ ଏବଂ ଆହୁରି ଅନେକ କିଛି କରନ୍ତୁ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ପାଇଁ ଅନ୍ୟ ଏକ ଆପକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ଏକ ଆପକୁ ରିପୋଜିସନ କରିବା ପାଇଁ ଏହାର ବାହାରେ ଦୁଇଥର-ଟାପ କରନ୍ତୁ"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ବଡ଼ କରନ୍ତୁ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ବାମରେ ସ୍ନାପ କରନ୍ତୁ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ଡାହାଣରେ ସ୍ନାପ କରନ୍ତୁ"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"ଡିଫଲ୍ଟ ସେଟିଂସକୁ ଖୋଲନ୍ତୁ"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ଏହି ଆପ ପାଇଁ ୱେବ ଲିଙ୍କଗୁଡ଼ିକୁ କିପରି ଖୋଲିବେ, ତାହା ବାଛନ୍ତୁ"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ଆପରେ"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ଆପଣଙ୍କ ବ୍ରାଉଜରରେ"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ଠିକ ଅଛି"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index 06e1f54e9e9b..f93f5097ac66 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ?\nਮੁੜ-ਫਿੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ਐਪ ਮੀਨੂ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ਕਈ ਐਪਾਂ ਇਕੱਠੀਆਂ ਦਿਖਾਉਣ ਲਈ ਟੈਪ ਕਰੋ"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ਐਪ ਮੀਨੂ ਤੋਂ ਪੂਰੀ-ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਵਾਪਸ ਜਾਓ"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ਐਪ ਮੀਨੂ ਇੱਥੇ ਮਿਲ ਸਕਦਾ ਹੈ"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ਕਈ ਐਪਾਂ ਨੂੰ ਇਕੱਠੇ ਖੋਲ੍ਹਣ ਲਈ ਡੈਸਕਟਾਪ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ਐਪ ਮੀਨੂ ਤੋਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਵਾਪਸ ਜਾਓ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ਦੇਖੋ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਕਰੋ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੇ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਵਿੱਚ ਘਸੀਟੋ"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ਕਿਸੇ ਐਪ ਦੀ ਜਗ੍ਹਾ ਬਦਲਣ ਲਈ ਉਸ ਦੇ ਬਾਹਰ ਡਬਲ ਟੈਪ ਕਰੋ"</string> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index 90d374cb446c..1e76e82094d8 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemy z aparatem?\nKliknij, aby dopasować"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Kliknij, aby otworzyć menu aplikacji"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Kliknij, aby wyświetlić jednocześnie kilka aplikacji"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Wróć do trybu pełnoekranowego z menu aplikacji"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Tu znajdziesz menu aplikacji"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Aby otworzyć kilka aplikacji jednocześnie, przejdź do widoku pulpitu"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Z menu aplikacji w każdej chwili możesz wrócić do pełnego ekranu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobacz i zrób więcej"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Aby podzielić ekran, przeciągnij drugą aplikację"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Kliknij dwukrotnie poza aplikacją, aby ją przenieść"</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml index a7cf9d87dbd3..0bdb0786ef71 100644 --- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toque para abrir o menu do app"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toque para mostrar vários apps juntos"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Volte para a tela cheia no menu do app"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu do app está aqui"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Acesse a versão para computadores para abrir vários apps ao mesmo tempo"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volte para a tela cheia a qualquer momento no menu do app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outro app para dividir a tela"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de um app para reposicionar"</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index 40241b4710ce..0e10da1e6e0a 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmara?\nToque aqui para reajustar"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toque para abrir o menu de apps"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toque para mostrar várias apps em conjunto"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Regresse ao ecrã inteiro a partir do menu de apps"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu da app pode ser encontrado aqui"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entre na vista de computador para abrir várias apps em conjunto"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Regresse ao ecrã inteiro em qualquer altura a partir do menu da app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outra app para usar o ecrã dividido"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de uma app para a reposicionar"</string> diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml index a7cf9d87dbd3..0bdb0786ef71 100644 --- a/libs/WindowManager/Shell/res/values-pt/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toque para abrir o menu do app"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toque para mostrar vários apps juntos"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Volte para a tela cheia no menu do app"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu do app está aqui"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Acesse a versão para computadores para abrir vários apps ao mesmo tempo"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volte para a tela cheia a qualquer momento no menu do app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outro app para dividir a tela"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de um app para reposicionar"</string> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index f323ff8f5109..c94273630432 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ai probleme cu camera foto?\nAtinge pentru a reîncadra"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ai remediat problema?\nAtinge pentru a reveni"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu ai probleme cu camera foto? Atinge pentru a închide."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Atinge pentru a deschide meniul aplicației"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Atinge pentru a afișa mai multe aplicații împreună"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Revino la ecranul complet din meniul aplicației"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meniul aplicației poate fi găsit aici"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Accesează afișarea pe desktop pentru a deschide mai multe aplicații simultan"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revino oricând la ecranul complet din meniul aplicației"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vezi și fă mai multe"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trage în altă aplicație pentru a folosi ecranul împărțit"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Atinge de două ori lângă o aplicație pentru a o repoziționa"</string> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index 45da723e5e97..e2c39387c8b4 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблемы с камерой?\nНажмите, чтобы исправить."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Нажмите, чтобы открыть меню приложения"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Нажмите, чтобы на экране размещались сразу несколько приложений"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Вернуться из меню приложения в режим полного экрана"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Здесь вы найдете меню приложения"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Чтобы открыть сразу несколько приложений, перейдите в режим компьютера"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вернуться в полноэкранный режим можно из меню приложения"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Выполняйте несколько задач одновременно"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Перетащите сюда другое приложение, чтобы использовать разделение экрана."</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Чтобы переместить приложение, дважды нажмите рядом с ним."</string> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 0b1a2390b892..83a09f5beffe 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"කැමරා ගැටලුද?\nයළි සවි කිරීමට තට්ටු කරන්න"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්රතිවර්තනය කිරීමට තට්ටු කරන්න"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"යෙදුම් මෙනුව විවෘත කිරීමට තට්ටු කරන්න"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"යෙදුම් කිහිපයක් එකට පෙන්වීමට තට්ටු කරන්න"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"යෙදුම් මෙනුවෙන් පූර්ණ තිරය වෙත ආපසු යන්න"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"යෙදුම් මෙනුව මෙතැනින් සොයා ගත හැක"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"යෙදුම් කිහිපයක් එකට විවෘත කිරීමට ඩෙස්ක්ටොප් දසුනට ඇතුළු වන්න"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"යෙදුම් මෙනුවෙන් ඕනෑම වේලාවක පූර්ණ තිරය වෙත ආපසු යන්න"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"බලන්න සහ තවත් දේ කරන්න"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"බෙදුම් තිරය සඳහා වෙනත් යෙදුමකට අදින්න"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"යෙදුමක් නැවත ස්ථානගත කිරීමට පිටතින් දෙවරක් තට්ටු කරන්න"</string> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index 7d1a408f3172..1b3907e5775b 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s kamerou?\nKlepnutím znova upravte."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Klepnúť a otvoriť tak ponuku aplikácií"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Klepnúť a zobraziť tak viacero aplikácií naraz"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Prejsť späť na celú obrazovku z ponuky aplikácií"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ponuku aplikácie nájdete tu"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Prejdite do zobrazenia v počítači a otvorte viac aplikácií naraz"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Z ponuky aplikácie sa môžete kedykoľvek vrátiť na celú obrazovku"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobrazte si a zvládnite toho viac"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Rozdelenú obrazovku môžete použiť presunutím do inej aplikácie"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvojitým klepnutím mimo aplikácie zmeníte jej pozíciu"</string> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index a50397f0e973..0a1b4a691313 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Težave s fotoaparatom?\nDotaknite se za vnovično prilagoditev"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Dotaknite se, če želite odpreti meni aplikacije"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Dotaknite se, če želite prikazati več aplikacij hkrati"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Nazaj v celozaslonski način iz menija aplikacije"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meni aplikacije najdete tukaj"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Preklopite v pogled za namizni računalnik, če želite odpreti več aplikacij hkrati"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"V meniju aplikacije se lahko kadar koli vrnete v celozaslonski način"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Oglejte si in naredite več"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Za razdeljeni zaslon povlecite sem še eno aplikacijo."</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvakrat se dotaknite zunaj aplikacije, če jo želite prestaviti."</string> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index 480e2a4d7f68..75120d2418b5 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ka probleme me kamerën?\nTrokit për ta ripërshtatur"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Trokit për të hapur menynë e aplikacionit"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Trokit për të shfaqur disa aplikacione bashkë"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Kthehu tek ekrani i plotë nga menyja e aplikacionit"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menyja e aplikacioneve mund të gjendet këtu"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Kalo te pamja e desktopit për të hapur disa aplikacione së bashku"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kthehu tek ekrani i plotë në çdo kohë nga menyja e aplikacioneve"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Shiko dhe bëj më shumë"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Zvarrite në një aplikacion tjetër për ekranin e ndarë"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Trokit dy herë jashtë një aplikacioni për ta ripozicionuar"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizo"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Zhvendos majtas"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Zhvendos djathtas"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Hap sipas cilësimeve të parazgjedhura"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Zgjidh si do t\'i hapësh lidhjet e uebit për këtë aplikacion"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Në aplikacion"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Në shfletuesin tënd"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Në rregull"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index d8debc01cae2..8b5c4dfff363 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблема са камером?\nДодирните да бисте поново уклопили"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Додирните да бисте отворили мени апликације"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Додирните да бисте приказали више апликација заједно"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Вратите се из менија апликације на приказ преко целог екрана"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Мени апликације можете да пронађете овде"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Уђите у приказ за рачунаре да бисте истовремено отворили више апликација"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вратите се на цео екран било када из менија апликације"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Видите и урадите више"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Превуците другу апликацију да бисте користили подељени екран"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Двапут додирните изван апликације да бисте променили њену позицију"</string> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index e262a9b477d3..e40b6492fc71 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problem med kameran?\nTryck för att anpassa på nytt"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tryck för att öppna appmenyn"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tryck för att visa flera appar tillsammans"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Återgå till helskärm från appmenyn"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Appmenyn finns här"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Starta datorvyn för att öppna flera appar samtidigt"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Återgå till helskärm när som helst från appmenyn"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se och gör mer"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dra till en annan app för att dela upp skärmen"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tryck snabbt två gånger utanför en app för att flytta den"</string> @@ -136,7 +136,7 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Utöka"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fäst till vänster"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fäst till höger"</string> - <string name="open_by_default_settings_text" msgid="2526548548598185500">"Inställningar för öppna som standard"</string> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Inställningar för Öppna som standard"</string> <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Välj hur webblänkar ska öppnas för den här appen"</string> <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I webbläsaren"</string> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index b1679c078aac..e63229ccf2cd 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Je, kuna hitilafu za kamera?\nGusa ili urekebishe"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Gusa ili ufungue menyu ya programu"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Gusa ili uonyeshe programu nyingi kwa pamoja"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Rudi kwenye skrini nzima katika menyu ya programu"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Unaweza kupata menyu ya programu hapa"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Tumia mwonekano wa kompyuta ili ufungue programu nyingi pamoja"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Rudi kwenye skrini nzima wakati wowote ukitumia menyu ya programu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Angalia na ufanye zaidi"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Buruta katika programu nyingine ili utumie skrini iliyogawanywa"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Gusa mara mbili nje ya programu ili uihamishe"</string> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index 8df170df794b..95972f1f9486 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"கேமரா தொடர்பான சிக்கல்களா?\nமீண்டும் பொருத்த தட்டவும்"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ஆப்ஸ் மெனுவைத் திறக்க தட்டவும்"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"பல ஆப்ஸை ஒன்றாகக் காட்ட தட்டவும்"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ஆப்ஸ் மெனுவில் இருந்து முழுத்திரைக்குச் செல்லும்"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ஆப்ஸ் மெனுவை இங்கே பார்க்கலாம்"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"பல ஆப்ஸை ஒன்றாகத் திறக்க டெஸ்க்டாப் காட்சிக்குச் செல்லலாம்"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ஆப்ஸ் மெனுவிலிருந்து எப்போது வேண்டுமானாலும் முழுத்திரைக்குத் திரும்பலாம்"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"பலவற்றைப் பார்த்தல் மற்றும் செய்தல்"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"திரைப் பிரிப்புக்கு மற்றொரு ஆப்ஸை இழுக்கலாம்"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ஆப்ஸை இடம் மாற்ற அதன் வெளியில் இருமுறை தட்டலாம்"</string> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 82523b68308a..6223c83d7599 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"కెమెరా సమస్యలు ఉన్నాయా?\nరీఫిట్ చేయడానికి ట్యాప్ చేయండి"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"యాప్ మెనూని తెరవడానికి ట్యాప్ చేయండి"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"పలు యాప్లను కలిపి చూడటానికి ట్యాప్ చేయండి"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"యాప్ మెనూ నుండి ఫుల్ స్క్రీన్కు తిరిగి రండి"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"యాప్ మెనూను ఇక్కడ పొందవచ్చు"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"పలు యాప్లను ఒకేసారి తెరవడానికి డెస్క్టాప్ వీక్షణకు ఎంటర్ అవ్వండి"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"యాప్ మెనూ నుండి ఏ సమయంలోనైనా ఫుల్ స్క్రీన్కు తిరిగి రండి"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"చూసి, మరిన్ని చేయండి"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"స్ప్లిట్ స్క్రీన్ కోసం మరొక యాప్లోకి లాగండి"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"యాప్ స్థానాన్ని మార్చడానికి దాని వెలుపల డబుల్-ట్యాప్ చేయండి"</string> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index cc7a60384bf6..f74499c0ddbf 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"หากพบปัญหากับกล้อง\nแตะเพื่อแก้ไข"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"แตะเพื่อเปิดเมนูแอป"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"แตะเพื่อแสดงแอปหลายรายการพร้อมกัน"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"กลับไปที่เต็มหน้าจอจากเมนูแอป"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ดูเมนูแอปที่นี่ได้"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"เข้าสู่มุมมองบนเดสก์ท็อปเพื่อเปิดหลายแอปพร้อมกัน"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"กลับไปที่โหมดเต็มหน้าจอได้ทุกเมื่อจากเมนูแอป"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"รับชมและทำสิ่งต่างๆ ได้มากขึ้น"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ลากไปไว้ในแอปอื่นเพื่อแยกหน้าจอ"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"แตะสองครั้งด้านนอกแอปเพื่อเปลี่ยนตำแหน่ง"</string> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index bb543f3f6ee9..7d984e0a1c14 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"May mga isyu sa camera?\nI-tap para i-refit"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"I-tap para buksan ang menu ng app"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"I-tap para ipakita nang magkakasama ang maraming app"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Bumalik sa fullscreen mula sa menu ng app"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Makikita rito ang menu ng app"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Pumasok sa desktop view para magbukas ng maraming app nang sabay-sabay"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Bumalik sa full screen anumang oras mula sa menu ng app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Tumingin at gumawa ng higit pa"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Mag-drag ng isa pang app para sa split screen"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Mag-double tap sa labas ng app para baguhin ang posisyon nito"</string> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index c8dcefbe3728..ba186aae80c8 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kameranızda sorun mu var?\nDüzeltmek için dokunun"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Uygulama menüsünü açmak için dokunun"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Birden fazla uygulamayı birlikte göstermek için dokunun"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Uygulama menüsünden tam ekrana dönün"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Uygulama menüsünü burada bulabilirsiniz"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Birden fazla uygulamayı birlikte açmak için masaüstü görünümüne geçin"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Uygulama menüsünden dilediğiniz zaman tam ekrana dönebilirsiniz"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daha fazlasını görün ve yapın"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Bölünmüş ekran için başka bir uygulamayı sürükleyin"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Yeniden konumlandırmak için uygulamanın dışına iki kez dokunun"</string> diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml index 90a3bc33b3e2..756e64da4574 100644 --- a/libs/WindowManager/Shell/res/values-uk/strings.xml +++ b/libs/WindowManager/Shell/res/values-uk/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми з камерою?\nНатисніть, щоб пристосувати"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Натисніть, щоб відкрити меню додатка"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Натисніть, щоб переглянути кілька додатків одночасно"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Повернутися з меню додатка в повноекранний режим"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Тут ви знайдете меню додатка"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Щоб відкрити кілька додатків одночасно, перейдіть у режим робочого стола"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"З меню додатка можна будь-коли повернутися в повноекранний режим"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Більше простору та можливостей"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Щоб перейти в режим розділення екрана, перетягніть сюди інший додаток"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Щоб перемістити додаток, двічі торкніться області поза ним"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Розгорнути"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Закріпити ліворуч"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Закріпити праворуч"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Налаштування \"Відкривати за умовчанням\""</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Виберіть, як відкривати вебпосилання в цьому додатку"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У додатку"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У вебпереглядачі"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index eca6801a6af3..8aaa306a2ada 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"کیمرے کے مسائل؟\nدوبارہ فٹ کرنے کیلئے تھپتھپائیں"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ایپ مینو کھولنے کیلئے تھپتھپائیں"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"متعدد ایپس ایک ساتھ دکھانے کیلئے تھپتھپائیں"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ایپ مینو سے مکمل اسکرین پر واپس جائیں"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ایپ کا مینو یہاں پایا جا سکتا ہے"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"متعدد ایپس کو ایک ساتھ کھولنے کے لیے ڈیسک ٹاپ منظر میں داخل ہوں"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ایپ مینو سے کسی بھی وقت فُل اسکرین پر واپس جائیں"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"دیکھیں اور بہت کچھ کریں"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"اسپلٹ اسکرین کے ليے دوسری ایپ میں گھسیٹیں"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"کسی ایپ کی پوزیشن تبدیل کرنے کے لیے اس ایپ کے باہر دو بار تھپتھپائیں"</string> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index 4b163f79f970..4e4a58ba25dc 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera nosozmi?\nQayta moslash uchun bosing"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Ilova menyusini ochish uchun bosing"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Bir nechta ilovani birga chiqarish uchun bosing"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Ilova menyusidan butun ekranga qayting"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ilova menyusi shu yerda chiqadi"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Bir nechta ilovani birga ochish uchun kompyuter versiyasiga kiring"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ilova menyusi orqali istalganda butun ekranga qaytish mumkin"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Yana boshqa amallar"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Ekranni ikkiga ajratish uchun boshqa ilovani bu yerga torting"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Qayta joylash uchun ilova tashqarisiga ikki marta bosing"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Yoyish"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chapga tortish"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Oʻngga tortish"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Birlamchi sozlamalar asosida ochish"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Bu ilovalardagi veb havolalar qanday ochilishini tanlang"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Ilovada"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauzerda"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index e381c9860738..09a143af61a0 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Có vấn đề với máy ảnh?\nHãy nhấn để sửa lỗi"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Nhấn để mở trình đơn ứng dụng"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Nhấn để hiển thị nhiều ứng dụng cùng lúc"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Quay lại chế độ toàn màn hình từ trình đơn ứng dụng"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Bạn có thể tìm thấy trình đơn ứng dụng tại đây"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Chuyển sang chế độ xem trên máy tính để bàn để mở nhiều ứng dụng cùng lúc"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Trở về chế độ toàn màn hình bất cứ lúc nào từ trình đơn ứng dụng"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Xem và làm được nhiều việc hơn"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Kéo một ứng dụng khác vào để chia đôi màn hình"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Nhấn đúp bên ngoài ứng dụng để đặt lại vị trí"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml index e39a64df95fc..795febb0ee3f 100644 --- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相机有问题?\n点按即可整修"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"点按可打开应用菜单"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"点按可同时显示多个应用"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"从应用菜单可返回到全屏"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"您可以在此处找到应用菜单"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"进入桌面版视图可同时打开多个应用"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"随时从应用菜单返回全屏模式"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"查看和处理更多任务"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一个应用,即可使用分屏模式"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"在某个应用外连续点按两次,即可调整它的位置"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index 6cd2567bda45..0c6ad618ec58 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題?\n輕按即可修正"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"輕按即可開啟應用程式選單"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"輕按即可同時顯示多個應用程式"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"從應用程式選單返回全螢幕"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可在這裡找到應用程式選單"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"進入桌面電腦檢視模式以同時開啟多個應用程式"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你可隨時從應用程式選單返回全螢幕"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一個應用程式即可分割螢幕"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"在應用程式外輕按兩下即可調整位置"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index c14f66449a0e..442a6feae787 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題嗎?\n輕觸即可修正"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"輕觸即可開啟應用程式選單"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"輕觸即可一次顯示多個應用程式"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"從應用程式選單返回全螢幕"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可以在這裡查看應用程式選單"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"進入電腦檢視畫面可以同時開啟多個應用程式"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你隨時可以從應用程式選單返回全螢幕模式"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖進另一個應用程式即可使用分割畫面模式"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"在應用程式外輕觸兩下即可調整位置"</string> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index 70a35422982a..47613d451a33 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -97,9 +97,9 @@ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Izinkinga zekhamera?\nThepha ukuze uyilinganise kabusha"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string> - <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Thepha ukuze uvule imenyu ye-app"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Thepha ukuze ubonise ama-app amaningi ndawonye"</string> - <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Buyela esikrinini esigcwele ukusuka kumenyu ye-app"</string> + <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Imenyu ye-app ingatholakala lapha"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Faka ukubuka kwedeskithophu ukuze uvule ama-app amaningi ndawonye"</string> + <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Buyela esikrinini esigcwele noma nini ukusuka kumenyu ye-app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Bona futhi wenze okuningi"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Hudula kwenye i-app mayelana nokuhlukanisa isikrini"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Thepha kabili ngaphandle kwe-app ukuze uyimise kabusha"</string> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 1f1565160965..df1e2248872b 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -492,8 +492,12 @@ <dimen name="desktop_mode_maximize_menu_buttons_outline_stroke">1dp</dimen> <!-- The radius of the inner fill of the maximize menu buttons. --> <dimen name="desktop_mode_maximize_menu_buttons_fill_radius">4dp</dimen> - <!-- The padding between the outline and fill of the maximize menu buttons. --> - <dimen name="desktop_mode_maximize_menu_buttons_fill_padding">4dp</dimen> + <!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. --> + <dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding">4dp</dimen> + <!-- The vertical padding between the outline and fill of the maximize menu restore button. --> + <dimen name="desktop_mode_maximize_menu_restore_button_fill_vertical_padding">13dp</dimen> + <!-- The horizontal padding between the outline and fill of the maximize menu restore button. --> + <dimen name="desktop_mode_maximize_menu_restore_button_fill_horizontal_padding">21dp</dimen> <!-- The corner radius of the maximize menu. --> <dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 621e2aacd673..afac9f6433a3 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -319,6 +319,8 @@ <string name="desktop_mode_non_resizable_snap_text">App can\'t be moved here</string> <!-- Accessibility text for the Maximize Menu's maximize button [CHAR LIMIT=NONE] --> <string name="desktop_mode_maximize_menu_maximize_button_text">Maximize</string> + <!-- Accessibility text for the Maximize Menu's restore button [CHAR LIMIT=NONE] --> + <string name="desktop_mode_maximize_menu_restore_button_text">Restore</string> <!-- Accessibility text for the Maximize Menu's snap left button [CHAR LIMIT=NONE] --> <string name="desktop_mode_maximize_menu_snap_left_button_text">Snap left</string> <!-- Accessibility text for the Maximize Menu's snap right button [CHAR LIMIT=NONE] --> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index e3fc5c2273e2..e8e25e20d8d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -572,6 +572,7 @@ public class Bubble implements BubbleViewProvider { * @param expandedViewManager the bubble expanded view manager. * @param taskViewFactory the task view factory used to create the task view for the bubble. * @param positioner the bubble positioner. + * @param bubbleLogger log bubble metrics. * @param stackView the view the bubble is added to, iff showing as floating. * @param layerView the layer the bubble is added to, iff showing in the bubble bar. * @param iconFactory the icon factory used to create images for the bubble. @@ -581,6 +582,7 @@ public class Bubble implements BubbleViewProvider { BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory taskViewFactory, BubblePositioner positioner, + BubbleLogger bubbleLogger, @Nullable BubbleStackView stackView, @Nullable BubbleBarLayerView layerView, BubbleIconFactory iconFactory, @@ -595,6 +597,7 @@ public class Bubble implements BubbleViewProvider { expandedViewManager, taskViewFactory, positioner, + bubbleLogger, stackView, layerView, iconFactory, @@ -616,6 +619,7 @@ public class Bubble implements BubbleViewProvider { expandedViewManager, taskViewFactory, positioner, + bubbleLogger, stackView, layerView, iconFactory, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index c81ffdabf9b8..37e8ead4fc78 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -892,7 +892,7 @@ public class BubbleController implements ConfigurationChangeListener, registerBroadcastReceiver(); if (isShowingAsBubbleBar()) { mBubbleData.getOverflow().initializeForBubbleBar( - mExpandedViewManager, mBubblePositioner); + mExpandedViewManager, mBubblePositioner, mLogger); } else { mBubbleData.getOverflow().initialize( mExpandedViewManager, mStackView, mBubblePositioner); @@ -1100,6 +1100,7 @@ public class BubbleController implements ConfigurationChangeListener, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, + mLogger, mStackView, mLayerView, mBubbleIconFactory, @@ -1111,6 +1112,7 @@ public class BubbleController implements ConfigurationChangeListener, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, + mLogger, mStackView, mLayerView, mBubbleIconFactory, @@ -1246,9 +1248,12 @@ public class BubbleController implements ConfigurationChangeListener, */ public void dragBubbleToDismiss(String bubbleKey, long timestamp) { String selectedBubbleKey = mBubbleData.getSelectedBubbleKey(); - if (mBubbleData.hasAnyBubbleWithKey(bubbleKey)) { + Bubble bubbleToDismiss = mBubbleData.getAnyBubbleWithkey(bubbleKey); + if (bubbleToDismiss != null) { mBubbleData.dismissBubbleWithKey( bubbleKey, Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, timestamp); + mLogger.log(bubbleToDismiss, + BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE); } if (mBubbleData.hasBubbles()) { // We still have bubbles, if we dragged an individual bubble to dismiss we were expanded @@ -1582,6 +1587,7 @@ public class BubbleController implements ConfigurationChangeListener, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, + mLogger, mStackView, mLayerView, mBubbleIconFactory, @@ -1644,6 +1650,7 @@ public class BubbleController implements ConfigurationChangeListener, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, + mLogger, mStackView, mLayerView, mBubbleIconFactory, @@ -1724,6 +1731,7 @@ public class BubbleController implements ConfigurationChangeListener, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, + mLogger, mStackView, mLayerView, mBubbleIconFactory, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 709a7bdc61f2..4de9dfa54c5d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -495,7 +495,7 @@ public class BubbleData { /** * When this method is called it is expected that all info in the bubble has completed loading. * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager, - * BubbleTaskViewFactory, BubblePositioner, BubbleStackView, + * BubbleTaskViewFactory, BubblePositioner, BubbleLogger, BubbleStackView, * com.android.wm.shell.bubbles.bar.BubbleBarLayerView, * com.android.launcher3.icons.BubbleIconFactory, boolean) */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index 68c4657f2b68..c74412b825d9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -73,17 +73,19 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl fun initializeForBubbleBar( expandedViewManager: BubbleExpandedViewManager, - positioner: BubblePositioner + positioner: BubblePositioner, + bubbleLogger: BubbleLogger, ) { createBubbleBarExpandedView() .initialize( expandedViewManager, positioner, + bubbleLogger, /* isOverflow= */ true, /* bubbleTaskView= */ null, /* mainExecutor= */ null, /* backgroundExecutor= */ null, - /* regionSamplingProvider= */ null + /* regionSamplingProvider= */ null, ) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index 39fb2f49c1ee..96b6043059d2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -73,6 +73,7 @@ public class BubbleViewInfoTask { private final WeakReference<BubbleExpandedViewManager> mExpandedViewManager; private final WeakReference<BubbleTaskViewFactory> mTaskViewFactory; private final WeakReference<BubblePositioner> mPositioner; + private final WeakReference<BubbleLogger> mBubbleLogger; private final WeakReference<BubbleStackView> mStackView; private final WeakReference<BubbleBarLayerView> mLayerView; private final BubbleIconFactory mIconFactory; @@ -94,6 +95,7 @@ public class BubbleViewInfoTask { BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory taskViewFactory, BubblePositioner positioner, + BubbleLogger bubbleLogger, @Nullable BubbleStackView stackView, @Nullable BubbleBarLayerView layerView, BubbleIconFactory factory, @@ -106,6 +108,7 @@ public class BubbleViewInfoTask { mExpandedViewManager = new WeakReference<>(expandedViewManager); mTaskViewFactory = new WeakReference<>(taskViewFactory); mPositioner = new WeakReference<>(positioner); + mBubbleLogger = new WeakReference<>(bubbleLogger); mStackView = new WeakReference<>(stackView); mLayerView = new WeakReference<>(layerView); mIconFactory = factory; @@ -221,8 +224,9 @@ public class BubbleViewInfoTask { ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing bubble bar expanded view key=%s", mBubble.getKey()); viewInfo.bubbleBarExpandedView.initialize(mExpandedViewManager.get(), - mPositioner.get(), false /* isOverflow */, viewInfo.taskView, - mMainExecutor, mBgExecutor, new RegionSamplingProvider() { + mPositioner.get(), mBubbleLogger.get(), false /* isOverflow */, + viewInfo.taskView, mMainExecutor, mBgExecutor, + new RegionSamplingProvider() { @Override public RegionSamplingHelper createHelper(View sampledView, RegionSamplingHelper.SamplingCallback callback, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java index e9a593392dc2..c1da94cc470f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java @@ -78,6 +78,7 @@ public class BubbleViewInfoTaskLegacy extends private WeakReference<BubbleExpandedViewManager> mExpandedViewManager; private WeakReference<BubbleTaskViewFactory> mTaskViewFactory; private WeakReference<BubblePositioner> mPositioner; + private WeakReference<BubbleLogger> mBubbleLogger; private WeakReference<BubbleStackView> mStackView; private WeakReference<BubbleBarLayerView> mLayerView; private BubbleIconFactory mIconFactory; @@ -95,6 +96,7 @@ public class BubbleViewInfoTaskLegacy extends BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory taskViewFactory, BubblePositioner positioner, + BubbleLogger bubbleLogger, @Nullable BubbleStackView stackView, @Nullable BubbleBarLayerView layerView, BubbleIconFactory factory, @@ -107,6 +109,7 @@ public class BubbleViewInfoTaskLegacy extends mExpandedViewManager = new WeakReference<>(expandedViewManager); mTaskViewFactory = new WeakReference<>(taskViewFactory); mPositioner = new WeakReference<>(positioner); + mBubbleLogger = new WeakReference<>(bubbleLogger); mStackView = new WeakReference<>(stackView); mLayerView = new WeakReference<>(layerView); mIconFactory = factory; @@ -124,8 +127,9 @@ public class BubbleViewInfoTaskLegacy extends } if (mLayerView.get() != null) { return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(), - mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory, - mBubble, mSkipInflation, mMainExecutor, mBackgroundExecutor); + mTaskViewFactory.get(), mPositioner.get(), mBubbleLogger.get(), + mLayerView.get(), mIconFactory, mBubble, mSkipInflation, mMainExecutor, + mBackgroundExecutor); } else { return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(), mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory, @@ -187,6 +191,7 @@ public class BubbleViewInfoTaskLegacy extends BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory taskViewFactory, BubblePositioner positioner, + BubbleLogger bubbleLogger, BubbleBarLayerView layerView, BubbleIconFactory iconFactory, Bubble b, @@ -200,9 +205,9 @@ public class BubbleViewInfoTaskLegacy extends LayoutInflater inflater = LayoutInflater.from(c); info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate( R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */); - info.bubbleBarExpandedView.initialize( - expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView, - mainExecutor, backgroundExecutor, new RegionSamplingProvider() { + info.bubbleBarExpandedView.initialize(expandedViewManager, positioner, bubbleLogger, + false /* isOverflow */, bubbleTaskView, mainExecutor, backgroundExecutor, + new RegionSamplingProvider() { @Override public RegionSamplingHelper createHelper(View sampledView, RegionSamplingHelper.SamplingCallback callback, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 2a9001728cc2..0ce651c3f1fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -39,6 +39,7 @@ import androidx.annotation.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.bubbles.BubbleExpandedViewManager; +import com.android.wm.shell.bubbles.BubbleLogger; import com.android.wm.shell.bubbles.BubbleOverflowContainerView; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleTaskView; @@ -90,6 +91,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private Bubble mBubble; private BubbleExpandedViewManager mManager; private BubblePositioner mPositioner; + private BubbleLogger mBubbleLogger; private boolean mIsOverflow; private BubbleTaskViewHelper mBubbleTaskViewHelper; private BubbleBarMenuViewController mMenuViewController; @@ -178,6 +180,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView /** Initializes the view, must be called before doing anything else. */ public void initialize(BubbleExpandedViewManager expandedViewManager, BubblePositioner positioner, + BubbleLogger bubbleLogger, boolean isOverflow, @Nullable BubbleTaskView bubbleTaskView, @Nullable Executor mainExecutor, @@ -185,6 +188,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Nullable RegionSamplingProvider regionSamplingProvider) { mManager = expandedViewManager; mPositioner = positioner; + mBubbleLogger = bubbleLogger; mIsOverflow = isOverflow; mMainExecutor = mainExecutor; mBackgroundExecutor = backgroundExecutor; @@ -248,6 +252,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onDismissBubble(Bubble bubble) { mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE); + mBubbleLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java index 0300869cbbe1..52b807abddd6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles.bar; import android.annotation.ColorInt; +import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; @@ -41,6 +42,9 @@ import java.util.ArrayList; * Bubble bar expanded view menu */ public class BubbleBarMenuView extends LinearLayout { + + public static final Object DISMISS_ACTION_TAG = new Object(); + private ViewGroup mBubbleSectionView; private ViewGroup mActionsSectionView; private ImageView mBubbleIconView; @@ -119,6 +123,9 @@ public class BubbleBarMenuView extends LinearLayout { R.layout.bubble_bar_menu_item, mActionsSectionView, false); itemView.update(action.mIcon, action.mTitle, action.mTint); itemView.setOnClickListener(action.mOnClick); + if (action.mTag != null) { + itemView.setTag(action.mTag); + } mActionsSectionView.addView(itemView); } } @@ -159,6 +166,8 @@ public class BubbleBarMenuView extends LinearLayout { private Icon mIcon; private @ColorInt int mTint; private String mTitle; + @Nullable + private Object mTag; private OnClickListener mOnClick; MenuAction(Icon icon, String title, OnClickListener onClick) { @@ -171,5 +180,14 @@ public class BubbleBarMenuView extends LinearLayout { this.mTint = tint; this.mOnClick = onClick; } + + MenuAction(Icon icon, String title, @ColorInt int tint, @Nullable Object tag, + OnClickListener onClick) { + this.mIcon = icon; + this.mTitle = title; + this.mTint = tint; + this.mTag = tag; + this.mOnClick = onClick; + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java index 514810745e10..5ed01b66ec67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java @@ -212,6 +212,7 @@ class BubbleBarMenuViewController { Icon.createWithResource(resources, R.drawable.ic_remove_no_shadow), resources.getString(R.string.bubble_dismiss_text), tintColor, + BubbleBarMenuView.DISMISS_ACTION_TAG, view -> { hideMenu(true /* animated */); if (mListener != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java index 0b2b3e7c41c8..85921703d559 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java @@ -87,7 +87,7 @@ public class DividerSnapAlgorithm { private final boolean mCalculateRatiosBasedOnAvailableSpace; /** Allows split ratios that go offscreen (a.k.a. "flexible split") */ private final boolean mAllowOffscreenRatios; - private final boolean mIsHorizontalDivision; + private final boolean mIsLeftRightSplit; /** The first target which is still splitting the screen */ private final SnapTarget mFirstSplitTarget; @@ -101,13 +101,13 @@ public class DividerSnapAlgorithm { public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize, - boolean isHorizontalDivision, Rect insets, int dockSide) { - this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets, - dockSide, false /* minimized */, true /* resizable */); + boolean isLeftRightSplit, Rect insets, int dockSide) { + this(res, displayWidth, displayHeight, dividerSize, isLeftRightSplit, insets, + dockSide, false /* minimized */, true /* resizable */); } public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize, - boolean isHorizontalDivision, Rect insets, int dockSide, boolean isMinimizedMode, + boolean isLeftRightSplit, Rect insets, int dockSide, boolean isMinimizedMode, boolean isHomeResizable) { mMinFlingVelocityPxPerSecond = MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density; @@ -116,7 +116,7 @@ public class DividerSnapAlgorithm { mDividerSize = dividerSize; mDisplayWidth = displayWidth; mDisplayHeight = displayHeight; - mIsHorizontalDivision = isHorizontalDivision; + mIsLeftRightSplit = isLeftRightSplit; mInsets.set(insets); mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED : res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode); @@ -133,7 +133,7 @@ public class DividerSnapAlgorithm { Flags.enableFlexibleTwoAppSplit() && SplitScreenUtils.allowOffscreenRatios(res); mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize( com.android.internal.R.dimen.task_height_of_minimized_mode) : 0; - calculateTargets(isHorizontalDivision, dockSide); + calculateTargets(isLeftRightSplit, dockSide); mFirstSplitTarget = mTargets.get(1); mLastSplitTarget = mTargets.get(mTargets.size() - 2); mDismissStartTarget = mTargets.get(0); @@ -218,18 +218,18 @@ public class DividerSnapAlgorithm { } private int getStartInset() { - if (mIsHorizontalDivision) { - return mInsets.top; - } else { + if (mIsLeftRightSplit) { return mInsets.left; + } else { + return mInsets.top; } } private int getEndInset() { - if (mIsHorizontalDivision) { - return mInsets.bottom; - } else { + if (mIsLeftRightSplit) { return mInsets.right; + } else { + return mInsets.bottom; } } @@ -269,11 +269,11 @@ public class DividerSnapAlgorithm { return mTargets.get(minIndex); } - private void calculateTargets(boolean isHorizontalDivision, int dockedSide) { + private void calculateTargets(boolean isLeftRightSplit, int dockedSide) { mTargets.clear(); - int dividerMax = isHorizontalDivision - ? mDisplayHeight - : mDisplayWidth; + int dividerMax = isLeftRightSplit + ? mDisplayWidth + : mDisplayHeight; int startPos = -mDividerSize; if (dockedSide == DOCKED_RIGHT) { startPos += mInsets.left; @@ -281,38 +281,38 @@ public class DividerSnapAlgorithm { mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f)); switch (mSnapMode) { case SNAP_MODE_16_9: - addRatio16_9Targets(isHorizontalDivision, dividerMax); + addRatio16_9Targets(isLeftRightSplit, dividerMax); break; case SNAP_FIXED_RATIO: - addFixedDivisionTargets(isHorizontalDivision, dividerMax); + addFixedDivisionTargets(isLeftRightSplit, dividerMax); break; case SNAP_ONLY_1_1: - addMiddleTarget(isHorizontalDivision); + addMiddleTarget(isLeftRightSplit); break; case SNAP_MODE_MINIMIZED: - addMinimizedTarget(isHorizontalDivision, dockedSide); + addMinimizedTarget(isLeftRightSplit, dockedSide); break; } mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f)); } - private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition, + private void addNonDismissingTargets(boolean isLeftRightSplit, int topPosition, int bottomPosition, int dividerMax) { @PersistentSnapPosition int firstTarget = mAllowOffscreenRatios ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66; @PersistentSnapPosition int lastTarget = mAllowOffscreenRatios ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33; maybeAddTarget(topPosition, topPosition - getStartInset(), firstTarget); - addMiddleTarget(isHorizontalDivision); + addMiddleTarget(isLeftRightSplit); maybeAddTarget(bottomPosition, dividerMax - getEndInset() - (bottomPosition + mDividerSize), lastTarget); } - private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) { - int start = isHorizontalDivision ? mInsets.top : mInsets.left; - int end = isHorizontalDivision - ? mDisplayHeight - mInsets.bottom - : mDisplayWidth - mInsets.right; + private void addFixedDivisionTargets(boolean isLeftRightSplit, int dividerMax) { + int start = isLeftRightSplit ? mInsets.left : mInsets.top; + int end = isLeftRightSplit + ? mDisplayWidth - mInsets.right + : mDisplayHeight - mInsets.bottom; int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2; if (mAllowOffscreenRatios) { @@ -323,23 +323,23 @@ public class DividerSnapAlgorithm { } int topPosition = start + size; int bottomPosition = end - size - mDividerSize; - addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax); + addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax); } - private void addRatio16_9Targets(boolean isHorizontalDivision, int dividerMax) { - int start = isHorizontalDivision ? mInsets.top : mInsets.left; - int end = isHorizontalDivision - ? mDisplayHeight - mInsets.bottom - : mDisplayWidth - mInsets.right; - int startOther = isHorizontalDivision ? mInsets.left : mInsets.top; - int endOther = isHorizontalDivision + private void addRatio16_9Targets(boolean isLeftRightSplit, int dividerMax) { + int start = isLeftRightSplit ? mInsets.left : mInsets.top; + int end = isLeftRightSplit ? mDisplayWidth - mInsets.right : mDisplayHeight - mInsets.bottom; + int startOther = isLeftRightSplit ? mInsets.top : mInsets.left; + int endOther = isLeftRightSplit + ? mDisplayHeight - mInsets.bottom + : mDisplayWidth - mInsets.right; float size = 9.0f / 16.0f * (endOther - startOther); int sizeInt = (int) Math.floor(size); int topPosition = start + sizeInt; int bottomPosition = end - sizeInt - mDividerSize; - addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax); + addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax); } /** @@ -352,17 +352,17 @@ public class DividerSnapAlgorithm { } } - private void addMiddleTarget(boolean isHorizontalDivision) { - int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, + private void addMiddleTarget(boolean isLeftRightSplit) { + int position = DockedDividerUtils.calculateMiddlePosition(isLeftRightSplit, mInsets, mDisplayWidth, mDisplayHeight, mDividerSize); mTargets.add(new SnapTarget(position, SNAP_TO_2_50_50)); } - private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) { + private void addMinimizedTarget(boolean isLeftRightSplit, int dockedSide) { // In portrait offset the position by the statusbar height, in landscape add the statusbar // height as well to match portrait offset int position = mTaskHeightInMinimizedMode + mInsets.top; - if (!isHorizontalDivision) { + if (isLeftRightSplit) { if (dockedSide == DOCKED_LEFT) { position += mInsets.left; } else if (dockedSide == DOCKED_RIGHT) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java index f25dfeafb32c..25157c05d0de 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java @@ -97,12 +97,12 @@ public class DockedDividerUtils { } } - public static int calculateMiddlePosition(boolean isHorizontalDivision, Rect insets, + public static int calculateMiddlePosition(boolean isLeftRightSplit, Rect insets, int displayWidth, int displayHeight, int dividerSize) { - int start = isHorizontalDivision ? insets.top : insets.left; - int end = isHorizontalDivision - ? displayHeight - insets.bottom - : displayWidth - insets.right; + int start = isLeftRightSplit ? insets.left : insets.top; + int end = isLeftRightSplit + ? displayWidth - insets.right + : displayHeight - insets.bottom; return start + (end - start) / 2 - dividerSize / 2; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index c9c3aa0dd537..f73065ea8eb8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -665,7 +665,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange rootBounds.width(), rootBounds.height(), mDividerSize, - !mIsLeftRightSplit, + mIsLeftRightSplit, insets, mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 7a9d9734baa0..03a851bb9507 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -16,6 +16,7 @@ package com.android.wm.shell.dagger; +import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS; import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS; import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT; @@ -728,7 +729,8 @@ public abstract class WMShellModule { Transitions transitions, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, InteractionJankMonitor interactionJankMonitor) { - return Flags.enableDesktopWindowingTransitions() + return (Flags.enableDesktopWindowingTransitions() || + ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue()) ? new SpringDragToDesktopTransitionHandler(context, transitions, rootTaskDisplayAreaOrganizer, interactionJankMonitor) : new DefaultDragToDesktopTransitionHandler(context, transitions, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt index 679179a7ff68..320c003f41eb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt @@ -23,6 +23,7 @@ import android.os.IBinder import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.view.animation.DecelerateInterpolator +import android.window.DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS import android.window.TransitionInfo import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction @@ -102,10 +103,8 @@ class DesktopFullImmersiveTransitionHandler( return } - val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return - val destinationBounds = calculateMaximizeBounds(displayLayout, taskInfo) val wct = WindowContainerTransaction().apply { - setBounds(taskInfo.token, destinationBounds) + setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) } logV("Moving task ${taskInfo.taskId} out of immersive mode") val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ this) @@ -145,11 +144,10 @@ class DesktopFullImmersiveTransitionHandler( displayId: Int ): ((IBinder) -> Unit)? { if (!Flags.enableFullyImmersiveInDesktop()) return null - val displayLayout = displayController.getDisplayLayout(displayId) ?: return null val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId) ?: return null val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask) ?: return null logV("Appending immersive exit for task: $immersiveTask in display: $displayId") - wct.setBounds(taskInfo.token, calculateMaximizeBounds(displayLayout, taskInfo)) + wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) return { transition -> addPendingImmersiveExit(immersiveTask, displayId, transition) } } @@ -168,16 +166,14 @@ class DesktopFullImmersiveTransitionHandler( if (desktopRepository.isTaskInFullImmersiveState(taskInfo.taskId)) { // A full immersive task is being minimized, make sure the immersive state is broken // (i.e. resize back to max bounds). - displayController.getDisplayLayout(taskInfo.displayId)?.let { displayLayout -> - wct.setBounds(taskInfo.token, calculateMaximizeBounds(displayLayout, taskInfo)) - logV("Appending immersive exit for task: ${taskInfo.taskId}") - return { transition -> - addPendingImmersiveExit( - taskId = taskInfo.taskId, - displayId = taskInfo.displayId, - transition = transition - ) - } + wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) + logV("Appending immersive exit for task: ${taskInfo.taskId}") + return { transition -> + addPendingImmersiveExit( + taskId = taskInfo.taskId, + displayId = taskInfo.displayId, + transition = transition + ) } } return null @@ -302,14 +298,19 @@ class DesktopFullImmersiveTransitionHandler( taskId = pendingExit.taskId, immersive = false ) + if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) { + desktopRepository.removeBoundsBeforeFullImmersive(pendingExit.taskId) + } } } return } // Check if this is a direct immersive enter/exit transition. - val state = this.state ?: return - if (transition == state.transition) { + if (transition == state?.transition) { + val state = requireState() + val startBounds = info.changes.first { c -> c.taskInfo?.taskId == state.taskId } + .startAbsBounds logV("Direct move for task ${state.taskId} in ${state.direction} direction verified") when (state.direction) { Direction.ENTER -> { @@ -318,6 +319,9 @@ class DesktopFullImmersiveTransitionHandler( taskId = state.taskId, immersive = true ) + if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) { + desktopRepository.saveBoundsBeforeFullImmersive(state.taskId, startBounds) + } } Direction.EXIT -> { desktopRepository.setTaskInFullImmersiveState( @@ -325,15 +329,48 @@ class DesktopFullImmersiveTransitionHandler( taskId = state.taskId, immersive = false ) + if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) { + desktopRepository.removeBoundsBeforeFullImmersive(state.taskId) + } } } + return } + + // Check if this is an untracked exit transition, like display rotation. + info.changes + .filter { c -> c.taskInfo != null } + .filter { c -> desktopRepository.isTaskInFullImmersiveState(c.taskInfo!!.taskId) } + .filter { c -> c.startRotation != c.endRotation } + .forEach { c -> + logV("Detected immersive exit due to rotation for task: ${c.taskInfo!!.taskId}") + desktopRepository.setTaskInFullImmersiveState( + displayId = c.taskInfo!!.displayId, + taskId = c.taskInfo!!.taskId, + immersive = false + ) + } } private fun clearState() { state = null } + private fun getExitDestinationBounds(taskInfo: RunningTaskInfo): Rect { + val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) + ?: error("Expected non-null display layout for displayId: ${taskInfo.displayId}") + return if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) { + desktopRepository.removeBoundsBeforeFullImmersive(taskInfo.taskId) + ?: if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) { + calculateInitialBounds(displayLayout, taskInfo) + } else { + calculateDefaultDesktopTaskBounds(displayLayout) + } + } else { + return calculateMaximizeBounds(displayLayout, taskInfo) + } + } + private fun requireState(): TransitionState = state ?: error("Expected non-null transition state") diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt index 379e052e7b38..8ebe503a3816 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -21,17 +21,39 @@ import com.android.internal.protolog.ProtoLog import com.android.internal.util.FrameworkStatsLog import com.android.window.flags.Flags import com.android.wm.shell.EventLogTags -import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import java.security.SecureRandom +import java.util.Random +import java.util.concurrent.atomic.AtomicInteger + /** Event logger for logging desktop mode session events */ class DesktopModeEventLogger { + private val random: Random = SecureRandom() + + /** The session id for the current desktop mode session */ + @VisibleForTesting + val currentSessionId: AtomicInteger = AtomicInteger(NO_SESSION_ID) + + private fun generateSessionId() = 1 + random.nextInt(1 shl 20) + /** - * Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for - * entering desktop mode + * Logs enter into desktop mode with [enterReason] */ - fun logSessionEnter(sessionId: Int, enterReason: EnterReason) { + fun logSessionEnter(enterReason: EnterReason) { + val sessionId = generateSessionId() + val previousSessionId = currentSessionId.getAndSet(sessionId) + if (previousSessionId != NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Existing desktop mode session id: %s found on desktop " + + "mode enter", + previousSessionId + ) + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging session enter, session: %s reason: %s", sessionId, enterReason.name @@ -47,12 +69,20 @@ class DesktopModeEventLogger { } /** - * Logs the exit of desktop mode having session id [sessionId] and the reason [exitReason] for - * exiting desktop mode + * Logs exit from desktop mode session with [exitReason] */ - fun logSessionExit(sessionId: Int, exitReason: ExitReason) { + fun logSessionExit(exitReason: ExitReason) { + val sessionId = currentSessionId.getAndSet(NO_SESSION_ID) + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging exit from desktop mode" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging session exit, session: %s reason: %s", sessionId, exitReason.name @@ -68,12 +98,20 @@ class DesktopModeEventLogger { } /** - * Logs that the task with update [taskUpdate] was added in the desktop mode session having - * session id [sessionId] + * Logs that a task with [taskUpdate] was added in a desktop mode session */ - fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) { + fun logTaskAdded(taskUpdate: TaskUpdate) { + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging task added" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task added, session: %s taskId: %s", sessionId, taskUpdate.instanceId @@ -85,12 +123,20 @@ class DesktopModeEventLogger { } /** - * Logs that the task with update [taskUpdate] was removed in the desktop mode session having - * session id [sessionId] + * Logs that a task with [taskUpdate] was removed from a desktop mode session */ - fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) { + fun logTaskRemoved(taskUpdate: TaskUpdate) { + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging task removed" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task remove, session: %s taskId: %s", sessionId, taskUpdate.instanceId @@ -102,12 +148,20 @@ class DesktopModeEventLogger { } /** - * Logs that the task with update [taskUpdate] had it's info changed in the desktop mode session - * having session id [sessionId] + * Logs that a task with [taskUpdate] had it's info changed in a desktop mode session */ - fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) { + fun logTaskInfoChanged(taskUpdate: TaskUpdate) { + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging task info changed" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task info changed, session: %s taskId: %s", sessionId, taskUpdate.instanceId @@ -119,14 +173,23 @@ class DesktopModeEventLogger { } /** - * Logs that a task resize event is starting with [taskSizeUpdate] within a - * Desktop mode [sessionId]. + * Logs that a task resize event is starting with [taskSizeUpdate] within a Desktop mode + * session. */ - fun logTaskResizingStarted(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) { + fun logTaskResizingStarted(taskSizeUpdate: TaskSizeUpdate) { if (!Flags.enableResizingMetrics()) return + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging start of task resizing" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task resize is starting, session: %s taskId: %s", sessionId, taskSizeUpdate.instanceId @@ -138,14 +201,22 @@ class DesktopModeEventLogger { } /** - * Logs that a task resize event is ending with [taskSizeUpdate] within a - * Desktop mode [sessionId]. + * Logs that a task resize event is ending with [taskSizeUpdate] within a Desktop mode session. */ - fun logTaskResizingEnded(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) { + fun logTaskResizingEnded(taskSizeUpdate: TaskSizeUpdate) { if (!Flags.enableResizingMetrics()) return + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging end of task resizing" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task resize is ending, session: %s taskId: %s", sessionId, taskSizeUpdate.instanceId @@ -248,6 +319,7 @@ class DesktopModeEventLogger { } companion object { + /** * Describes a task position and dimensions. * @@ -465,5 +537,6 @@ class DesktopModeEventLogger { FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE private const val DESKTOP_MODE_TASK_SIZE_UPDATED_ATOM_ID = FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED + @VisibleForTesting const val NO_SESSION_ID = 0 } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt index f847aa8918c2..ed03982d191d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt @@ -35,8 +35,6 @@ import androidx.core.util.isEmpty import androidx.core.util.isNotEmpty import androidx.core.util.plus import androidx.core.util.putAll -import com.android.internal.logging.InstanceId -import com.android.internal.logging.InstanceIdSequence import com.android.internal.protolog.ProtoLog import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason @@ -48,8 +46,8 @@ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE -import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.TransitionUtil +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions @@ -65,8 +63,6 @@ class DesktopModeLoggerTransitionObserver( private val desktopModeEventLogger: DesktopModeEventLogger ) : Transitions.TransitionObserver { - private val idSequence: InstanceIdSequence by lazy { InstanceIdSequence(Int.MAX_VALUE) } - init { if (DesktopModeStatus.canEnterDesktopMode(context)) { shellInit.addInitCallback(this::onInit, this) @@ -87,15 +83,7 @@ class DesktopModeLoggerTransitionObserver( // following enter reason could be Screen On private var wasPreviousTransitionExitByScreenOff: Boolean = false - // The instanceId for the current logging session - private var loggerInstanceId: InstanceId? = null - - private val isSessionActive: Boolean - get() = loggerInstanceId != null - - private fun setSessionInactive() { - loggerInstanceId = null - } + @VisibleForTesting var isSessionActive: Boolean = false fun onInit() { transitions.registerObserver(this) @@ -247,38 +235,32 @@ class DesktopModeLoggerTransitionObserver( ) { // Sessions is finishing, log task updates followed by an exit event identifyAndLogTaskUpdates( - loggerInstanceId!!.id, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks ) desktopModeEventLogger.logSessionExit( - loggerInstanceId!!.id, getExitReason(transitionInfo) ) - - setSessionInactive() + isSessionActive = false } else if ( postTransitionVisibleFreeformTasks.isNotEmpty() && preTransitionVisibleFreeformTasks.isEmpty() && !isSessionActive ) { // Session is starting, log enter event followed by task updates - loggerInstanceId = idSequence.newInstanceId() + isSessionActive = true desktopModeEventLogger.logSessionEnter( - loggerInstanceId!!.id, getEnterReason(transitionInfo) ) identifyAndLogTaskUpdates( - loggerInstanceId!!.id, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks ) } else if (isSessionActive) { // Session is neither starting, nor finishing, log task updates if there are any identifyAndLogTaskUpdates( - loggerInstanceId!!.id, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks ) @@ -291,7 +273,6 @@ class DesktopModeLoggerTransitionObserver( /** Compare the old and new state of taskInfos and identify and log the changes */ private fun identifyAndLogTaskUpdates( - sessionId: Int, preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>, postTransitionVisibleFreeformTasks: SparseArray<TaskInfo> ) { @@ -302,7 +283,7 @@ class DesktopModeLoggerTransitionObserver( when { // new tasks added previousTaskInfo == null -> { - desktopModeEventLogger.logTaskAdded(sessionId, currentTaskUpdate) + desktopModeEventLogger.logTaskAdded(currentTaskUpdate) Trace.setCounter( Trace.TRACE_TAG_WINDOW_MANAGER, VISIBLE_TASKS_COUNTER_NAME, @@ -315,14 +296,14 @@ class DesktopModeLoggerTransitionObserver( // TODO(b/347935387): Log changes only once they are stable. buildTaskUpdateForTask(previousTaskInfo, postTransitionVisibleFreeformTasks.size()) != currentTaskUpdate -> - desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate) + desktopModeEventLogger.logTaskInfoChanged(currentTaskUpdate) } } // find old tasks that were removed preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo -> if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) { - desktopModeEventLogger.logTaskRemoved(sessionId, + desktopModeEventLogger.logTaskRemoved( buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size())) Trace.setCounter( Trace.TRACE_TAG_WINDOW_MANAGER, @@ -417,13 +398,6 @@ class DesktopModeLoggerTransitionObserver( visibleFreeformTaskInfos.set(taskInfo.taskId, taskInfo) } - @VisibleForTesting fun getLoggerSessionId(): Int? = loggerInstanceId?.id - - @VisibleForTesting - fun setLoggerSessionId(id: Int) { - loggerInstanceId = InstanceId.fakeInstanceId(id) - } - private fun TransitionInfo.Change.requireTaskInfo(): RunningTaskInfo { return this.taskInfo ?: throw IllegalStateException("Expected TaskInfo in the Change") } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt index 6d4792250be2..edcc877ef58e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt @@ -37,6 +37,23 @@ val DESKTOP_MODE_LANDSCAPE_APP_PADDING: Int = SystemProperties.getInt("persist.wm.debug.desktop_mode_landscape_app_padding", 25) /** + * Calculates the initial bounds to enter desktop, centered on the display. + */ +fun calculateDefaultDesktopTaskBounds(displayLayout: DisplayLayout): Rect { + // TODO(b/319819547): Account for app constraints so apps do not become letterboxed + val desiredWidth = (displayLayout.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt() + val desiredHeight = (displayLayout.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt() + val heightOffset = (displayLayout.height() - desiredHeight) / 2 + val widthOffset = (displayLayout.width() - desiredWidth) / 2 + return Rect( + widthOffset, + heightOffset, + desiredWidth + widthOffset, + desiredHeight + heightOffset + ) +} + +/** * Calculates the initial bounds required for an application to fill a scale of the display bounds * without any letterboxing. This is done by taking into account the applications fullscreen size, * aspect ratio, orientation and resizability to calculate an area this is compatible with the diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java index 52b92a89abdf..61de0777ed05 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java @@ -378,11 +378,12 @@ public class DesktopModeVisualIndicator { private static VisualIndicatorAnimator fadeBoundsIn( @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) { - final Rect startBounds = getIndicatorBounds(displayLayout, type); + final Rect endBounds = getIndicatorBounds(displayLayout, type); + final Rect startBounds = getMinBounds(endBounds); view.getBackground().setBounds(startBounds); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( - view, startBounds, getMaxBounds(startBounds)); + view, startBounds, endBounds); animator.setInterpolator(new DecelerateInterpolator()); setupIndicatorAnimation(animator, AlphaAnimType.ALPHA_FADE_IN_ANIM); return animator; @@ -390,8 +391,8 @@ public class DesktopModeVisualIndicator { private static VisualIndicatorAnimator fadeBoundsOut( @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) { - final Rect endBounds = getIndicatorBounds(displayLayout, type); - final Rect startBounds = getMaxBounds(endBounds); + final Rect startBounds = getIndicatorBounds(displayLayout, type); + final Rect endBounds = getMinBounds(startBounds); view.getBackground().setBounds(startBounds); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( @@ -422,28 +423,35 @@ public class DesktopModeVisualIndicator { return animator; } + /** Calculates the bounds the indicator should have when fully faded in. */ private static Rect getIndicatorBounds(DisplayLayout layout, IndicatorType type) { - final int padding = layout.stableInsets().top; + final Rect desktopStableBounds = new Rect(); + layout.getStableBounds(desktopStableBounds); + final int padding = desktopStableBounds.top; switch (type) { case TO_FULLSCREEN_INDICATOR: - return new Rect(padding, padding, - layout.width() - padding, - layout.height() - padding); + desktopStableBounds.top += padding; + desktopStableBounds.bottom -= padding; + desktopStableBounds.left += padding; + desktopStableBounds.right -= padding; + return desktopStableBounds; case TO_DESKTOP_INDICATOR: final float adjustmentPercentage = 1f - DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE; - return new Rect((int) (adjustmentPercentage * layout.width() / 2), - (int) (adjustmentPercentage * layout.height() / 2), - (int) (layout.width() - (adjustmentPercentage * layout.width() / 2)), - (int) (layout.height() - (adjustmentPercentage * layout.height() / 2))); + return new Rect((int) (adjustmentPercentage * desktopStableBounds.width() / 2), + (int) (adjustmentPercentage * desktopStableBounds.height() / 2), + (int) (desktopStableBounds.width() + - (adjustmentPercentage * desktopStableBounds.width() / 2)), + (int) (desktopStableBounds.height() + - (adjustmentPercentage * desktopStableBounds.height() / 2))); case TO_SPLIT_LEFT_INDICATOR: return new Rect(padding, padding, - layout.width() / 2 - padding, - layout.height() - padding); + desktopStableBounds.width() / 2 - padding, + desktopStableBounds.height()); case TO_SPLIT_RIGHT_INDICATOR: - return new Rect(layout.width() / 2 + padding, padding, - layout.width() - padding, - layout.height() - padding); + return new Rect(desktopStableBounds.width() / 2 + padding, padding, + desktopStableBounds.width() - padding, + desktopStableBounds.height()); default: throw new IllegalArgumentException("Invalid indicator type provided."); } @@ -505,17 +513,18 @@ public class DesktopModeVisualIndicator { } /** - * Return the max bounds of a visual indicator + * Return the minimum bounds of a visual indicator, to be used at the end of fading out + * and the start of fading in. */ - private static Rect getMaxBounds(Rect startBounds) { - return new Rect((int) (startBounds.left - - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())), - (int) (startBounds.top - - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())), - (int) (startBounds.right - + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())), - (int) (startBounds.bottom - + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height()))); + private static Rect getMinBounds(Rect maxBounds) { + return new Rect((int) (maxBounds.left + + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())), + (int) (maxBounds.top + + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height())), + (int) (maxBounds.right + - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())), + (int) (maxBounds.bottom + - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height()))); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt index 5ac4ef5cf049..eeb7ac852070 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt @@ -102,6 +102,9 @@ class DesktopRepository ( /* Tracks last bounds of task before toggled to stable bounds. */ private val boundsBeforeMaximizeByTaskId = SparseArray<Rect>() + /* Tracks last bounds of task before toggled to immersive state. */ + private val boundsBeforeFullImmersiveByTaskId = SparseArray<Rect>() + private var desktopGestureExclusionListener: Consumer<Region>? = null private var desktopGestureExclusionExecutor: Executor? = null @@ -414,6 +417,7 @@ class DesktopRepository ( logD("Removes freeform task: taskId=%d, displayId=%d", taskId, displayId) desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.remove(taskId) boundsBeforeMaximizeByTaskId.remove(taskId) + boundsBeforeFullImmersiveByTaskId.remove(taskId) logD("Remaining freeform tasks: %s", desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.toDumpString()) // Remove task from unminimized task if it is minimized. @@ -472,6 +476,14 @@ class DesktopRepository ( fun saveBoundsBeforeMaximize(taskId: Int, bounds: Rect) = boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds)) + /** Removes and returns the bounds saved before entering immersive with the given task. */ + fun removeBoundsBeforeFullImmersive(taskId: Int): Rect? = + boundsBeforeFullImmersiveByTaskId.removeReturnOld(taskId) + + /** Saves the bounds of the given task before entering immersive. */ + fun saveBoundsBeforeFullImmersive(taskId: Int, bounds: Rect) = + boundsBeforeFullImmersiveByTaskId.set(taskId, Rect(bounds)) + private fun updatePersistentRepository(displayId: Int) { // Create a deep copy of the data desktopTaskDataByDisplayId[displayId]?.deepCopy()?.let { desktopTaskDataByDisplayIdCopy -> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 57a59c946f98..29e302a0f0cc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -82,6 +82,7 @@ import com.android.wm.shell.desktopmode.DesktopRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener +import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler import com.android.wm.shell.draganddrop.DragAndDropController import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.recents.RecentTasksController @@ -562,10 +563,20 @@ class DesktopTasksController( ) } - /** Move a task to the front */ - fun moveTaskToFront(taskId: Int) { + /** + * Move a task to the front, using [remoteTransition]. + * + * Note: beyond moving a task to the front, this method will minimize a task if we reach the + * Desktop task limit, so [remoteTransition] should also handle any such minimize change. + */ + @JvmOverloads + fun moveTaskToFront(taskId: Int, remoteTransition: RemoteTransition? = null) { val task = shellTaskOrganizer.getRunningTaskInfo(taskId) - if (task == null) moveBackgroundTaskToFront(taskId) else moveTaskToFront(task) + if (task == null) { + moveBackgroundTaskToFront(taskId, remoteTransition) + } else { + moveTaskToFront(task, remoteTransition) + } } /** @@ -573,12 +584,10 @@ class DesktopTasksController( * desktop. If outside of desktop and want to launch a background task in desktop, use * [moveBackgroundTaskToDesktop] instead. */ - private fun moveBackgroundTaskToFront(taskId: Int) { + private fun moveBackgroundTaskToFront(taskId: Int, remoteTransition: RemoteTransition?) { logV("moveBackgroundTaskToFront taskId=%s", taskId) val wct = WindowContainerTransaction() // TODO: b/342378842 - Instead of using default display, support multiple displays - val taskToMinimize: RunningTaskInfo? = - addAndGetMinimizeChangesIfNeeded(DEFAULT_DISPLAY, wct, taskId) val runOnTransit = immersiveTransitionHandler .exitImmersiveIfApplicable(wct, DEFAULT_DISPLAY) wct.startTask( @@ -587,26 +596,56 @@ class DesktopTasksController( launchWindowingMode = WINDOWING_MODE_FREEFORM }.toBundle(), ) - val transition = transitions.startTransition(TRANSIT_OPEN, wct, null /* handler */) - addPendingMinimizeTransition(transition, taskToMinimize) + val transition = startLaunchTransition(TRANSIT_OPEN, wct, taskId, remoteTransition) runOnTransit?.invoke(transition) } - /** Move a task to the front */ - fun moveTaskToFront(taskInfo: RunningTaskInfo) { + /** + * Move a task to the front, using [remoteTransition]. + * + * Note: beyond moving a task to the front, this method will minimize a task if we reach the + * Desktop task limit, so [remoteTransition] should also handle any such minimize change. + */ + @JvmOverloads + fun moveTaskToFront(taskInfo: RunningTaskInfo, remoteTransition: RemoteTransition? = null) { logV("moveTaskToFront taskId=%s", taskInfo.taskId) val wct = WindowContainerTransaction() wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */) val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable( wct, taskInfo.displayId) - val taskToMinimize = - addAndGetMinimizeChangesIfNeeded(taskInfo.displayId, wct, taskInfo.taskId) - - val transition = transitions.startTransition(TRANSIT_TO_FRONT, wct, null /* handler */) - addPendingMinimizeTransition(transition, taskToMinimize) + val transition = + startLaunchTransition(TRANSIT_TO_FRONT, wct, taskInfo.taskId, remoteTransition) runOnTransit?.invoke(transition) } + private fun startLaunchTransition( + transitionType: Int, + wct: WindowContainerTransaction, + taskId: Int, + remoteTransition: RemoteTransition?, + ): IBinder { + val taskToMinimize: RunningTaskInfo? = + addAndGetMinimizeChangesIfNeeded(DEFAULT_DISPLAY, wct, taskId) + if (remoteTransition == null) { + val t = transitions.startTransition(transitionType, wct, null /* handler */) + addPendingMinimizeTransition(t, taskToMinimize) + return t + } + if (taskToMinimize == null) { + val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition) + val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler) + remoteTransitionHandler.setTransition(t) + return t + } + val remoteTransitionHandler = + DesktopWindowLimitRemoteHandler( + mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskToMinimize.taskId) + val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler) + remoteTransitionHandler.setTransition(t) + addPendingMinimizeTransition(t, taskToMinimize) + return t + } + /** * Move task to the next display. * @@ -659,6 +698,7 @@ class DesktopTasksController( } val wct = WindowContainerTransaction() + if (!task.isFreeform) addMoveToDesktopChanges(wct, task, displayId) wct.reparent(task.token, displayAreaInfo.token, true /* onTop */) transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) @@ -711,7 +751,7 @@ class DesktopTasksController( if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) { destinationBounds.set(calculateInitialBounds(displayLayout, taskInfo)) } else { - destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout)) + destinationBounds.set(calculateDefaultDesktopTaskBounds(displayLayout)) } } } else { @@ -880,20 +920,6 @@ class DesktopTasksController( } } - private fun getDefaultDesktopTaskBounds(displayLayout: DisplayLayout): Rect { - // TODO(b/319819547): Account for app constraints so apps do not become letterboxed - val desiredWidth = (displayLayout.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt() - val desiredHeight = (displayLayout.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt() - val heightOffset = (displayLayout.height() - desiredHeight) / 2 - val widthOffset = (displayLayout.width() - desiredWidth) / 2 - return Rect( - widthOffset, - heightOffset, - desiredWidth + widthOffset, - desiredHeight + heightOffset - ) - } - private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect() @@ -1184,7 +1210,13 @@ class DesktopTasksController( val options = createNewWindowOptions(callingTask) if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) { wct.startTask(requestedTaskId, options.toBundle()) - transitions.startTransition(TRANSIT_OPEN, wct, null) + val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( + callingTask.displayId, wct, requestedTaskId) + val runOnTransit = immersiveTransitionHandler + .exitImmersiveIfApplicable(wct, callingTask.displayId) + val transition = transitions.startTransition(TRANSIT_OPEN, wct, null) + addPendingMinimizeTransition(transition, taskToMinimize) + runOnTransit?.invoke(transition) } else { val splitPosition = splitScreenController.determineNewInstancePosition(callingTask) splitScreenController.startTask(requestedTaskId, splitPosition, @@ -1218,7 +1250,8 @@ class DesktopTasksController( .determineNewInstancePosition(callingTaskInfo) splitScreenController.startIntent( launchIntent, context.userId, fillIn, splitPosition, - options.toBundle(), null /* hideTaskToken */ + options.toBundle(), null /* hideTaskToken */, + true /* forceLaunchNewTask */ ) } WINDOWING_MODE_FREEFORM -> { @@ -1245,7 +1278,7 @@ class DesktopTasksController( val bounds = when (newTaskWindowingMode) { WINDOWING_MODE_FREEFORM -> { displayController.getDisplayLayout(callingTask.displayId) - ?.let { getInitialBounds(it, callingTask) } + ?.let { getInitialBounds(it, callingTask, callingTask.displayId) } } WINDOWING_MODE_MULTI_WINDOW -> { Rect() @@ -1311,7 +1344,7 @@ class DesktopTasksController( val displayLayout = displayController.getDisplayLayout(task.displayId) if (displayLayout != null) { val initialBounds = Rect(task.configuration.windowConfiguration.bounds) - cascadeWindow(task, initialBounds, displayLayout) + cascadeWindow(initialBounds, displayLayout, task.displayId) wct.setBounds(task.token, initialBounds) } } @@ -1399,13 +1432,19 @@ class DesktopTasksController( return if (wct.isEmpty) null else wct } + /** + * Apply all changes required when task is first added to desktop. Uses the task's current + * display by default to apply initial bounds and placement relative to the display. + * Use a different [displayId] if the task should be moved to a different display. + */ @VisibleForTesting fun addMoveToDesktopChanges( wct: WindowContainerTransaction, - taskInfo: RunningTaskInfo + taskInfo: RunningTaskInfo, + displayId: Int = taskInfo.displayId, ) { - val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return - val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!! + val displayLayout = displayController.getDisplayLayout(displayId) ?: return + val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)!! val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) { @@ -1414,7 +1453,7 @@ class DesktopTasksController( } else { WINDOWING_MODE_FREEFORM } - val initialBounds = getInitialBounds(displayLayout, taskInfo) + val initialBounds = getInitialBounds(displayLayout, taskInfo, displayId) if (canChangeTaskPosition(taskInfo)) { wct.setBounds(taskInfo.token, initialBounds) @@ -1428,16 +1467,17 @@ class DesktopTasksController( private fun getInitialBounds( displayLayout: DisplayLayout, - taskInfo: RunningTaskInfo + taskInfo: RunningTaskInfo, + displayId: Int, ): Rect { val bounds = if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue) { calculateInitialBounds(displayLayout, taskInfo) } else { - getDefaultDesktopTaskBounds(displayLayout) + calculateDefaultDesktopTaskBounds(displayLayout) } if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue) { - cascadeWindow(taskInfo, bounds, displayLayout) + cascadeWindow(bounds, displayLayout, displayId) } return bounds } @@ -1466,11 +1506,11 @@ class DesktopTasksController( } } - private fun cascadeWindow(task: TaskInfo, bounds: Rect, displayLayout: DisplayLayout) { + private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) { val stableBounds = Rect() displayLayout.getStableBoundsForDesktopMode(stableBounds) - val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(task.displayId) + val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(displayId) activeTasks.firstOrNull()?.let { activeTask -> shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let { cascadeWindow(context.resources, stableBounds, @@ -1829,7 +1869,7 @@ class DesktopTasksController( when (indicatorType) { IndicatorType.TO_DESKTOP_INDICATOR -> { // Use default bounds, but with the top-center at the drop point. - newWindowBounds.set(getDefaultDesktopTaskBounds(displayLayout)) + newWindowBounds.set(calculateDefaultDesktopTaskBounds(displayLayout)) newWindowBounds.offsetTo( dragEvent.x.toInt() - (newWindowBounds.width() / 2), dragEvent.y.toInt() @@ -2016,9 +2056,9 @@ class DesktopTasksController( } } - override fun showDesktopApp(taskId: Int) { + override fun showDesktopApp(taskId: Int, remoteTransition: RemoteTransition?) { executeRemoteCallWithTaskPermission(controller, "showDesktopApp") { c -> - c.moveTaskToFront(taskId) + c.moveTaskToFront(taskId, remoteTransition) } } @@ -2069,6 +2109,12 @@ class DesktopTasksController( c.removeDesktop(displayId) } } + + override fun moveToExternalDisplay(taskId: Int) { + executeRemoteCallWithTaskPermission(controller, "moveTaskToExternalDisplay") { c -> + c.moveToNextDisplay(taskId) + } + } } private fun logV(msg: String, vararg arguments: Any?) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl index 86351e364cdd..aac2361f717e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -35,8 +35,13 @@ interface IDesktopMode { /** @deprecated this is no longer supported. */ void hideStashedDesktopApps(int displayId); - /** Bring task with the given id to front */ - oneway void showDesktopApp(int taskId); + /** + * Bring task with the given id to front, using the given remote transition. + * + * <p> Note: beyond moving a task to the front, this method will minimize a task if we reach the + * Desktop task limit, so {@code remoteTransition} should also handle any such minimize change. + */ + oneway void showDesktopApp(int taskId, in @nullable RemoteTransition remoteTransition); /** Get count of visible desktop tasks on the given display */ int getVisibleTaskCount(int displayId); @@ -52,4 +57,7 @@ interface IDesktopMode { /** Remove desktop on the given display */ oneway void removeDesktop(int displayId); + + /** Move a task with given `taskId` to external display */ + void moveToExternalDisplay(int taskId); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandler.kt new file mode 100644 index 000000000000..7554cbb96606 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandler.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode.minimize + +import android.os.IBinder +import android.view.SurfaceControl +import android.view.WindowManager.TRANSIT_TO_BACK +import android.window.RemoteTransition +import android.window.TransitionInfo +import android.window.TransitionInfo.Change +import android.window.TransitionRequestInfo +import android.window.WindowContainerTransaction +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.shared.TransitionUtil +import com.android.wm.shell.transition.OneShotRemoteHandler +import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.transition.Transitions.TransitionHandler + +/** + * Handles transitions that result in hitting the Desktop window limit, by performing some + * preparation work and then delegating to [remoteTransition]. + * + * This transition handler prepares the leash of the minimizing change referenced by + * [taskIdToMinimize], and then delegates to [remoteTransition] to perform the actual transition. + */ +class DesktopWindowLimitRemoteHandler( + mainExecutor: ShellExecutor, + private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer, + remoteTransition: RemoteTransition, + private val taskIdToMinimize: Int, + ) : TransitionHandler { + + private val oneShotRemoteHandler = OneShotRemoteHandler(mainExecutor, remoteTransition) + private var transition: IBinder? = null + + /** Sets the transition that will be handled - this must be called before [startAnimation]. */ + fun setTransition(transition: IBinder) { + this.transition = transition + oneShotRemoteHandler.setTransition(transition) + } + + override fun handleRequest( + transition: IBinder, + request: TransitionRequestInfo + ): WindowContainerTransaction? { + this.transition = transition + return oneShotRemoteHandler.handleRequest(transition, request) + } + + override fun startAnimation( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + finishCallback: Transitions.TransitionFinishCallback + ): Boolean { + if (transition != this.transition) return false + val minimizeChange = findMinimizeChange(info, taskIdToMinimize) ?: return false + // Reparent the minimize change back to the root task display area, so the minimizing window + // isn't shown in front of other windows. We do this here in Shell since Launcher doesn't + // have access to RootTaskDisplayAreaOrganizer. + applyMinimizeChangeReparenting(info, minimizeChange, startTransaction) + return oneShotRemoteHandler.startAnimation( + transition, info, startTransaction, finishTransaction, finishCallback) + } + + private fun applyMinimizeChangeReparenting( + info: TransitionInfo, + minimizeChange: Change, + startTransaction: SurfaceControl.Transaction, + ) { + val taskInfo = minimizeChange.taskInfo ?: return + if (taskInfo.isFreeform && TransitionUtil.isOpeningMode(info.type)) { + rootTaskDisplayAreaOrganizer.reparentToDisplayArea( + taskInfo.displayId, minimizeChange.leash, startTransaction) + } + } + + private fun findMinimizeChange( + info: TransitionInfo, + taskIdToMinimize: Int, + ): Change? = + info.changes.find { change -> + change.taskInfo?.taskId == taskIdToMinimize && change.mode == TRANSIT_TO_BACK } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index cbb08b804dfe..6da39951efbe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -463,7 +463,7 @@ public class PipTransition extends PipTransitionController { // so update fixed rotation state to default. mFixedRotationState = FIXED_ROTATION_UNDEFINED; - if (transition != mExitTransition) { + if (transition != mExitTransition && transition != mMoveToBackTransition) { return; } // This means an expand happened before enter-pip finished and we are now "merging" a @@ -477,8 +477,10 @@ public class PipTransition extends PipTransitionController { cancelled = true; } - // Unset exitTransition AFTER cancel so that finishResize knows we are merging. + // Unset both exitTransition and moveToBackTransition AFTER cancel so that + // finishResize knows we are merging. mExitTransition = null; + mMoveToBackTransition = null; if (!cancelled) return; final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo(); if (taskInfo != null) { @@ -515,7 +517,8 @@ public class PipTransition extends PipTransitionController { // means we're expecting the exit transition will be "merged" into another transition // (likely a remote like launcher), so don't fire the finish-callback here -- wait until // the exit transition is merged. - if ((mExitTransition == null || isAnimatingLocally()) && mFinishCallback != null) { + if ((mExitTransition == null || mMoveToBackTransition == null || isAnimatingLocally()) + && mFinishCallback != null) { final SurfaceControl leash = mPipOrganizer.getSurfaceControl(); final boolean hasValidLeash = leash != null && leash.isValid(); WindowContainerTransaction wct = null; @@ -665,17 +668,6 @@ public class PipTransition extends PipTransitionController { return null; } - @Nullable - private TransitionInfo.Change findFixedRotationChange(@NonNull TransitionInfo info) { - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getEndFixedRotation() != ROTATION_UNDEFINED) { - return change; - } - } - return null; - } - private void startExitAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 9b815817d4d3..94b344fb575a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -16,6 +16,7 @@ package com.android.wm.shell.pip; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_PIP; @@ -346,6 +347,21 @@ public abstract class PipTransitionController implements Transitions.TransitionH return false; } + /** + * Gets a change amongst the transition targets that is in a different final orientation than + * the display, signalling a potential fixed rotation transition. + */ + @Nullable + public TransitionInfo.Change findFixedRotationChange(@NonNull TransitionInfo info) { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getEndFixedRotation() != ROTATION_UNDEFINED) { + return change; + } + } + return null; + } + /** End the currently-playing PiP animation. */ public void end() { } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java index f40a87c39aef..5381a626ddcf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java @@ -16,10 +16,14 @@ package com.android.wm.shell.pip2.animation; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + import android.animation.Animator; import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.content.Context; +import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.Rect; import android.view.Surface; @@ -60,6 +64,11 @@ public class PipEnterAnimator extends ValueAnimator private final PointF mInitScale = new PointF(); private final PointF mInitPos = new PointF(); private final Rect mInitCrop = new Rect(); + private final PointF mInitActivityScale = new PointF(); + private final PointF mInitActivityPos = new PointF(); + + Matrix mTransformTensor = new Matrix(); + final float[] mMatrixTmp = new float[9]; public PipEnterAnimator(Context context, @NonNull SurfaceControl leash, @@ -101,14 +110,16 @@ public class PipEnterAnimator extends ValueAnimator mAnimationStartCallback.run(); } if (mStartTransaction != null) { - onEnterAnimationUpdate(mInitScale, mInitPos, mInitCrop, - 0f /* fraction */, mStartTransaction); + onEnterAnimationUpdate(0f /* fraction */, mStartTransaction); mStartTransaction.apply(); } } @Override public void onAnimationEnd(@NonNull Animator animation) { + if (mFinishTransaction != null) { + onEnterAnimationUpdate(1f /* fraction */, mFinishTransaction); + } if (mAnimationEndCallback != null) { mAnimationEndCallback.run(); } @@ -118,24 +129,42 @@ public class PipEnterAnimator extends ValueAnimator public void onAnimationUpdate(@NonNull ValueAnimator animation) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); final float fraction = getAnimatedFraction(); - onEnterAnimationUpdate(mInitScale, mInitPos, mInitCrop, fraction, tx); + onEnterAnimationUpdate(fraction, tx); tx.apply(); } + /** + * Updates the transaction to reflect the state of PiP leash at a certain fraction during enter. + * + * @param fraction the fraction of the animator going from 0f to 1f. + * @param tx the transaction to modify the transform of. + */ + public void onEnterAnimationUpdate(float fraction, SurfaceControl.Transaction tx) { + onEnterAnimationUpdate(mInitScale, mInitPos, mInitCrop, fraction, tx); + } + private void onEnterAnimationUpdate(PointF initScale, PointF initPos, Rect initCrop, float fraction, SurfaceControl.Transaction tx) { float scaleX = 1 + (initScale.x - 1) * (1 - fraction); float scaleY = 1 + (initScale.y - 1) * (1 - fraction); - tx.setScale(mLeash, scaleX, scaleY); - float posX = initPos.x + (mEndBounds.left - initPos.x) * fraction; float posY = initPos.y + (mEndBounds.top - initPos.y) * fraction; - tx.setPosition(mLeash, posX, posY); + + int normalizedRotation = mRotation; + if (normalizedRotation == ROTATION_270) { + normalizedRotation = -ROTATION_90; + } + float degrees = -normalizedRotation * 90f * fraction; Rect endCrop = new Rect(mEndBounds); endCrop.offsetTo(0, 0); mRectEvaluator.evaluate(fraction, initCrop, endCrop); tx.setCrop(mLeash, mAnimatedRect); + + mTransformTensor.setScale(scaleX, scaleY); + mTransformTensor.postTranslate(posX, posY); + mTransformTensor.postRotate(degrees); + tx.setMatrix(mLeash, mTransformTensor, mMatrixTmp); } // no-ops @@ -153,7 +182,22 @@ public class PipEnterAnimator extends ValueAnimator * calculated differently from generic transitions. * @param pipChange PiP change received as a transition target. */ - public void setEnterStartState(@NonNull TransitionInfo.Change pipChange) { + public void setEnterStartState(@NonNull TransitionInfo.Change pipChange, + @NonNull TransitionInfo.Change pipActivityChange) { + PipUtils.calcEndTransform(pipActivityChange, pipChange, mInitActivityScale, + mInitActivityPos); + if (mStartTransaction != null && pipActivityChange.getLeash() != null) { + mStartTransaction.setCrop(pipActivityChange.getLeash(), null); + mStartTransaction.setScale(pipActivityChange.getLeash(), mInitActivityScale.x, + mInitActivityScale.y); + mStartTransaction.setPosition(pipActivityChange.getLeash(), mInitActivityPos.x, + mInitActivityPos.y); + mFinishTransaction.setCrop(pipActivityChange.getLeash(), null); + mFinishTransaction.setScale(pipActivityChange.getLeash(), mInitActivityScale.x, + mInitActivityScale.y); + mFinishTransaction.setPosition(pipActivityChange.getLeash(), mInitActivityPos.x, + mInitActivityPos.y); + } PipUtils.calcStartTransform(pipChange, mInitScale, mInitPos, mInitCrop); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java index 8fa5aa933929..a93ef12cb7fa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java @@ -157,6 +157,7 @@ public class PipExpandAnimator extends ValueAnimator .shadow(tx, mLeash, false /* applyCornerRadius */); tx.apply(); } + private Rect getInsets(float fraction) { final Rect startInsets = mSourceRectHintInsets; final Rect endInsets = mZeroInsets; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java index 73be8db0ea8a..0427294579dc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java @@ -292,23 +292,34 @@ public class PipController implements ConfigurationChangeListener, setDisplayLayout(mDisplayController.getDisplayLayout(displayId)); if (!mPipTransitionState.isInPip()) { + // Skip the PiP-relevant updates if we aren't in a valid PiP state. + if (mPipTransitionState.isInFixedRotation()) { + ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "Fixed rotation flag shouldn't be set while in an invalid PiP state"); + } return; } mPipTouchHandler.updateMinMaxSize(mPipBoundsState.getAspectRatio()); - // Update the caches to reflect the new display layout in the movement bounds; - // temporarily update bounds to be at the top left for the movement bounds calculation. - Rect toBounds = new Rect(0, 0, - (int) Math.ceil(mPipBoundsState.getMaxSize().x * boundsScale), - (int) Math.ceil(mPipBoundsState.getMaxSize().y * boundsScale)); - mPipBoundsState.setBounds(toBounds); - mPipTouchHandler.updateMovementBounds(); - - // The policy is to keep PiP snap fraction invariant. - mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction); - mPipBoundsState.setBounds(toBounds); - t.setBounds(mPipTransitionState.mPipTaskToken, toBounds); + if (mPipTransitionState.isInFixedRotation()) { + // Do not change the bounds when in fixed rotation, but do update the movement bounds + // based on the current bounds state and potentially new display layout. + mPipTouchHandler.updateMovementBounds(); + mPipTransitionState.setInFixedRotation(false); + } else { + Rect toBounds = new Rect(0, 0, + (int) Math.ceil(mPipBoundsState.getMaxSize().x * boundsScale), + (int) Math.ceil(mPipBoundsState.getMaxSize().y * boundsScale)); + // Update the caches to reflect the new display layout in the movement bounds; + // temporarily update bounds to be at the top left for the movement bounds calculation. + mPipBoundsState.setBounds(toBounds); + mPipTouchHandler.updateMovementBounds(); + // The policy is to keep PiP snap fraction invariant. + mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction); + mPipBoundsState.setBounds(toBounds); + } + t.setBounds(mPipTransitionState.mPipTaskToken, mPipBoundsState.getBounds()); } private void setDisplayLayout(DisplayLayout layout) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java index a4a7973ef4bb..4d0432e1066e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java @@ -406,12 +406,9 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha // We need to remove the callback even if the shelf is visible, in case it the delayed // callback hasn't been executed yet to avoid the wrong final state. mMainExecutor.removeCallbacks(mMoveOnShelVisibilityChanged); - if (shelfVisible) { - mMoveOnShelVisibilityChanged.run(); - } else { - // Postpone moving in response to hide of Launcher in case there's another change - mMainExecutor.executeDelayed(mMoveOnShelVisibilityChanged, PIP_KEEP_CLEAR_AREAS_DELAY); - } + + // Postpone moving in response to hide of Launcher in case there's another change + mMainExecutor.executeDelayed(mMoveOnShelVisibilityChanged, PIP_KEEP_CLEAR_AREAS_DELAY); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index ac1567aba6e9..b286211eb6d0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -16,7 +16,9 @@ package com.android.wm.shell.pip2.phone; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; @@ -35,7 +37,7 @@ import android.annotation.NonNull; import android.app.ActivityManager; import android.app.PictureInPictureParams; import android.content.Context; -import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; @@ -313,6 +315,14 @@ public class PipTransition extends PipTransitionController implements if (pipChange == null) { return false; } + + // We expect the PiP activity as a separate change in a config-at-end transition. + TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info, + pipChange.getTaskInfo().getToken()); + if (pipActivityChange == null) { + return false; + } + SurfaceControl pipLeash = pipChange.getLeash(); Preconditions.checkNotNull(pipLeash, "Leash is null for swipe-up transition."); @@ -330,27 +340,27 @@ public class PipTransition extends PipTransitionController implements (destinationBounds.width() - overlaySize) / 2f, (destinationBounds.height() - overlaySize) / 2f); } - startTransaction.merge(finishTransaction); final int startRotation = pipChange.getStartRotation(); final int endRotation = mPipDisplayLayoutState.getRotation(); - if (endRotation != startRotation) { - boolean isClockwise = (endRotation - startRotation) == -ROTATION_270; - - // Display bounds were already updated to represent the final orientation, - // so we just need to readjust the origin, and perform rotation about (0, 0). - Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds(); - int originTranslateX = isClockwise ? 0 : -displayBounds.width(); - int originTranslateY = isClockwise ? -displayBounds.height() : 0; - - Matrix transformTensor = new Matrix(); - final float[] matrixTmp = new float[9]; - transformTensor.setTranslate(originTranslateX + destinationBounds.left, - originTranslateY + destinationBounds.top); - final float degrees = (endRotation - startRotation) * 90f; - transformTensor.postRotate(degrees); - startTransaction.setMatrix(pipLeash, transformTensor, matrixTmp); + final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0 + : startRotation - endRotation; + if (delta != ROTATION_0) { + mPipTransitionState.setInFixedRotation(true); + handleBoundsTypeFixedRotation(pipChange, pipActivityChange, endRotation); + } + + Rect sourceRectHint = null; + if (pipChange.getTaskInfo() != null + && pipChange.getTaskInfo().pictureInPictureParams != null) { + sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint(); } + + startTransaction.merge(finishTransaction); + PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash, + startTransaction, finishTransaction, destinationBounds, sourceRectHint, delta); + animator.setEnterStartState(pipChange, pipActivityChange); + animator.onEnterAnimationUpdate(1.0f /* fraction */, startTransaction); startTransaction.apply(); finishInner(); return true; @@ -388,7 +398,13 @@ public class PipTransition extends PipTransitionController implements return false; } - Rect startBounds = pipChange.getStartAbsBounds(); + // We expect the PiP activity as a separate change in a config-at-end transition. + TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info, + pipChange.getTaskInfo().getToken()); + if (pipActivityChange == null) { + return false; + } + Rect endBounds = pipChange.getEndAbsBounds(); SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash; Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition."); @@ -411,14 +427,62 @@ public class PipTransition extends PipTransitionController implements } } + final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info); + int startRotation = pipChange.getStartRotation(); + int endRotation = fixedRotationChange != null + ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED; + final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0 + : startRotation - endRotation; + + if (delta != ROTATION_0) { + mPipTransitionState.setInFixedRotation(true); + handleBoundsTypeFixedRotation(pipChange, pipActivityChange, + fixedRotationChange.getEndFixedRotation()); + } + PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash, - startTransaction, finishTransaction, endBounds, sourceRectHint, Surface.ROTATION_0); - animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange)); + startTransaction, finishTransaction, endBounds, sourceRectHint, delta); + animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange, + pipActivityChange)); animator.setAnimationEndCallback(this::finishInner); animator.start(); return true; } + private void handleBoundsTypeFixedRotation(TransitionInfo.Change pipTaskChange, + TransitionInfo.Change pipActivityChange, int endRotation) { + final Rect endBounds = pipTaskChange.getEndAbsBounds(); + final Rect endActivityBounds = pipActivityChange.getEndAbsBounds(); + int startRotation = pipTaskChange.getStartRotation(); + + // Cache the task to activity offset to potentially restore later. + Point activityEndOffset = new Point(endActivityBounds.left - endBounds.left, + endActivityBounds.top - endBounds.top); + + // If we are running a fixed rotation bounds enter PiP animation, + // then update the display layout rotation, and recalculate the end rotation bounds. + // Update the endBounds in place, so that the PiP change is up-to-date. + mPipDisplayLayoutState.rotateTo(endRotation); + float snapFraction = mPipBoundsAlgorithm.getSnapFraction( + mPipBoundsAlgorithm.getEntryDestinationBounds()); + mPipBoundsAlgorithm.applySnapFraction(endBounds, snapFraction); + mPipBoundsState.setBounds(endBounds); + + // Display bounds were already updated to represent the final orientation, + // so we just need to readjust the origin, and perform rotation about (0, 0). + boolean isClockwise = (endRotation - startRotation) == -ROTATION_270; + Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds(); + int originTranslateX = isClockwise ? 0 : -displayBounds.width(); + int originTranslateY = isClockwise ? -displayBounds.height() : 0; + endBounds.offset(originTranslateX, originTranslateY); + + // Update the activity end bounds in place as well, as this is used for transform + // calculation later. + endActivityBounds.offsetTo(endBounds.left + activityEndOffset.x, + endBounds.top + activityEndOffset.y); + } + + private boolean startAlphaTypeEnterAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @@ -533,6 +597,19 @@ public class PipTransition extends PipTransitionController implements } @Nullable + private TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info, + @NonNull WindowContainerToken parent) { + for (TransitionInfo.Change change : info.getChanges()) { + if (change.getTaskInfo() == null + && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END) + && change.getParent() != null && change.getParent().equals(parent)) { + return change; + } + } + return null; + } + + @Nullable private TransitionInfo.Change getChangeByToken(TransitionInfo info, WindowContainerToken token) { for (TransitionInfo.Change change : info.getChanges()) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java index a132796f4a84..ccdd66b5d1a8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java @@ -155,6 +155,8 @@ public class PipTransitionState { @Nullable private Runnable mOnIdlePipTransitionStateRunnable; + private boolean mInFixedRotation = false; + /** * An interface to track state updates as we progress through PiP transitions. */ @@ -256,7 +258,7 @@ public class PipTransitionState { private void maybeRunOnIdlePipTransitionStateCallback() { if (mOnIdlePipTransitionStateRunnable != null && isPipStateIdle()) { - mOnIdlePipTransitionStateRunnable.run(); + mMainHandler.post(mOnIdlePipTransitionStateRunnable); mOnIdlePipTransitionStateRunnable = null; } } @@ -303,6 +305,23 @@ public class PipTransitionState { } /** + * @return true if either in swipe or button-nav fixed rotation. + */ + public boolean isInFixedRotation() { + return mInFixedRotation; + } + + /** + * Sets the fixed rotation flag. + */ + public void setInFixedRotation(boolean inFixedRotation) { + mInFixedRotation = inFixedRotation; + if (!inFixedRotation) { + maybeRunOnIdlePipTransitionStateCallback(); + } + } + + /** * @return true if in swipe PiP to home. Note that this is true until overlay fades if used too. */ public boolean isInSwipePipToHomeTransition() { @@ -351,7 +370,7 @@ public class PipTransitionState { public boolean isPipStateIdle() { // This needs to be a valid in-PiP state that isn't a transient state. - return mState == ENTERED_PIP || mState == CHANGED_PIP_BOUNDS; + return (mState == ENTERED_PIP || mState == CHANGED_PIP_BOUNDS) && !isInFixedRotation(); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 9e39f440915c..a23b576beebc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -772,15 +772,25 @@ public class SplitScreenController implements SplitDragPolicy.Starter, instanceId); } + @Override + public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent, + @SplitPosition int position, @Nullable Bundle options, + @Nullable WindowContainerToken hideTaskToken) { + startIntent(intent, userId1, fillInIntent, position, options, hideTaskToken, + false /* forceLaunchNewTask */); + } + /** * Starts the given intent into split. + * * @param hideTaskToken If non-null, a task matching this token will be moved to back in the * same window container transaction as the starting of the intent. + * @param forceLaunchNewTask If true, this method will skip the check for a background task + * matching the intent and launch a new task. */ - @Override public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options, - @Nullable WindowContainerToken hideTaskToken) { + @Nullable WindowContainerToken hideTaskToken, boolean forceLaunchNewTask) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1, fillInIntent, position); @@ -798,8 +808,9 @@ public class SplitScreenController implements SplitDragPolicy.Starter, // To prevent accumulating large number of instances in the background, reuse task // in the background. If we don't explicitly reuse, new may be created even if the app // isn't multi-instance because WM won't automatically remove/reuse the previous instance - final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional - .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1, + final ActivityManager.RecentTaskInfo taskInfo = forceLaunchNewTask ? null : + mRecentTasksOptional + .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1, hideTaskToken)) .orElse(null); if (taskInfo != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index fea987ac53e2..3e76403de51b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -175,6 +175,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private static final String TAG = StageCoordinator.class.getSimpleName(); + // The duration in ms to prevent launch-adjacent from working after split screen is first + // entered + private static final int DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS = 1000; + private final StageTaskListener mMainStage; private final StageTaskListener mSideStage; @SplitPosition @@ -239,6 +243,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private SplitScreen.SplitInvocationListener mSplitInvocationListener; private Executor mSplitInvocationListenerExecutor; + // Re-enables launch-adjacent handling on the split root task. This needs to be a member + // because we will be posting and removing it from the handler. + private final Runnable mReEnableLaunchAdjacentOnRoot = () -> setLaunchAdjacentDisabled(false); + /** * Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support * CompatUI layouts. CompatUI is handled separately by MainStage and SideStage. @@ -2699,6 +2707,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } + /** + * Sets whether launch-adjacent is disabled or enabled. + */ + private void setLaunchAdjacentDisabled(boolean disabled) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setLaunchAdjacentDisabled: disabled=%b", disabled); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setDisableLaunchAdjacent(mRootTaskInfo.token, disabled); + mTaskOrganizer.applyTransaction(wct); + } + /** Starts the pending transition animation. */ public boolean startPendingAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @@ -2711,6 +2729,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitTransitions.isPendingEnter(transition)) { shouldAnimate = startPendingEnterAnimation(transition, mSplitTransitions.mPendingEnter, info, startTransaction, finishTransaction); + + // Disable launch adjacent after an enter animation to prevent cases where apps are + // incorrectly trampolining and incorrectly triggering a double launch-adjacent task + // launch (ie. main -> split -> main). See b/344216031 + setLaunchAdjacentDisabled(true); + mMainHandler.removeCallbacks(mReEnableLaunchAdjacentOnRoot); + mMainHandler.postDelayed(mReEnableLaunchAdjacentOnRoot, + DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS); } else if (mSplitTransitions.isPendingDismiss(transition)) { final SplitScreenTransitions.DismissSession dismiss = mSplitTransitions.mPendingDismiss; shouldAnimate = startPendingDismissAnimation( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index ae4bd1615ae1..6313231b449e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -244,8 +244,9 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { return; } mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); + mVisible = taskInfo.isVisible && taskInfo.isVisibleRequested; mCallbacks.onChildTaskStatusChanged(this, taskInfo.taskId, true /* present */, - taskInfo.isVisible && taskInfo.isVisibleRequested); + mVisible); } else { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo + "\n mRootTaskInfo: " + mRootTaskInfo); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 2c621b1f1a52..d3b7ca15856f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -514,8 +514,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin )); } else { mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData( - mTaskInfo, TaskInfoKt.getRequestingImmersive(mTaskInfo), inFullImmersive, - hasGlobalFocus + mTaskInfo, + TaskInfoKt.getRequestingImmersive(mTaskInfo), + inFullImmersive, + hasGlobalFocus, + /* maximizeHoverEnabled= */ canOpenMaximizeMenu( + /* animatingTaskResizeOrReposition= */ false) )); } Trace.endSection(); @@ -1616,8 +1620,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin void setAnimatingTaskResizeOrReposition(boolean animatingTaskResizeOrReposition) { if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) return; - asAppHeader(mWindowDecorViewHolder) - .setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition); + final boolean inFullImmersive = + mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId); + asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData( + mTaskInfo, + TaskInfoKt.getRequestingImmersive(mTaskInfo), + inFullImmersive, + isFocused(), + /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition))); } /** @@ -1634,6 +1644,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin asAppHeader(mWindowDecorViewHolder).onMaximizeWindowHoverEnter(); } + private boolean canOpenMaximizeMenu(boolean animatingTaskResizeOrReposition) { + if (!Flags.enableFullyImmersiveInDesktop()) { + return !animatingTaskResizeOrReposition; + } + final boolean inImmersiveAndRequesting = + mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId) + && TaskInfoKt.getRequestingImmersive(mTaskInfo); + return !animatingTaskResizeOrReposition && !inImmersiveAndRequesting; + } + @Override public String toString() { return "{" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt index 0cb219ae4b81..3ae5a1afc7e2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt @@ -62,6 +62,7 @@ import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.desktopmode.calculateMaximizeBounds import com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_LINEAR_IN import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer @@ -73,7 +74,8 @@ import java.util.function.Supplier /** * Menu that appears when user long clicks the maximize button. Gives the user the option to - * maximize the task or snap the task to the right or left half of the screen. + * maximize the task or restore previous task bounds from the maximized state and to snap the task + * to the right or left half of the screen. */ class MaximizeMenu( private val syncQueue: SyncTransactionQueue, @@ -176,6 +178,7 @@ class MaximizeMenu( "MaximizeMenu") maximizeMenuView = MaximizeMenuView( context = decorWindowContext, + sizeToggleDirection = getSizeToggleDirection(), menuHeight = menuHeight, menuPadding = menuPadding, ).also { menuView -> @@ -202,6 +205,18 @@ class MaximizeMenu( } } + private fun getSizeToggleDirection(): MaximizeMenuView.SizeToggleDirection { + val maximizeBounds = calculateMaximizeBounds( + displayController.getDisplayLayout(taskInfo.displayId)!!, + taskInfo + ) + val maximized = taskInfo.configuration.windowConfiguration.bounds.equals(maximizeBounds) + return if (maximized) + MaximizeMenuView.SizeToggleDirection.RESTORE + else + MaximizeMenuView.SizeToggleDirection.MAXIMIZE + } + private fun loadDimensionPixelSize(resourceId: Int): Int { return if (resourceId == Resources.ID_NULL) { 0 @@ -236,18 +251,19 @@ class MaximizeMenu( * resizing a Task. */ class MaximizeMenuView( - context: Context, + private val context: Context, + private val sizeToggleDirection: SizeToggleDirection, private val menuHeight: Int, - private val menuPadding: Int, + private val menuPadding: Int ) { val rootView = LayoutInflater.from(context) .inflate(R.layout.desktop_mode_window_decor_maximize_menu, null /* root */) as ViewGroup private val container = requireViewById(R.id.container) private val overlay = requireViewById(R.id.maximize_menu_overlay) - private val maximizeText = - requireViewById(R.id.maximize_menu_maximize_window_text) as TextView - private val maximizeButton = - requireViewById(R.id.maximize_menu_maximize_button) as Button + private val sizeToggleButtonText = + requireViewById(R.id.maximize_menu_size_toggle_button_text) as TextView + private val sizeToggleButton = + requireViewById(R.id.maximize_menu_size_toggle_button) as Button private val snapWindowText = requireViewById(R.id.maximize_menu_snap_window_text) as TextView private val snapRightButton = @@ -263,8 +279,6 @@ class MaximizeMenu( .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_outline_radius) private val outlineStroke = context.resources .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_outline_stroke) - private val fillPadding = context.resources - .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_padding) private val fillRadius = context.resources .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_radius) @@ -324,7 +338,7 @@ class MaximizeMenu( return@setOnHoverListener false } - maximizeButton.setOnClickListener { onMaximizeClickListener?.invoke() } + sizeToggleButton.setOnClickListener { onMaximizeClickListener?.invoke() } snapRightButton.setOnClickListener { onRightSnapClickListener?.invoke() } snapLeftButton.setOnClickListener { onLeftSnapClickListener?.invoke() } rootView.setOnTouchListener { _, event -> @@ -335,9 +349,17 @@ class MaximizeMenu( true } + val btnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE) + R.string.desktop_mode_maximize_menu_restore_button_text + else + R.string.desktop_mode_maximize_menu_maximize_button_text + val btnText = context.resources.getText(btnTextId) + sizeToggleButton.contentDescription = btnText + sizeToggleButtonText.text = btnText + // To prevent aliasing. - maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null) - maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null) + sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null) + sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null) } /** Bind the menu views to the new [RunningTaskInfo] data. */ @@ -348,8 +370,8 @@ class MaximizeMenu( rootView.background.setTint(style.backgroundColor) // Maximize option. - maximizeButton.background = style.maximizeOption.drawable - maximizeText.setTextColor(style.textColor) + sizeToggleButton.background = style.maximizeOption.drawable + sizeToggleButtonText.setTextColor(style.textColor) // Snap options. snapWindowText.setTextColor(style.textColor) @@ -358,8 +380,8 @@ class MaximizeMenu( /** Animate the opening of the menu */ fun animateOpenMenu(onEnd: () -> Unit) { - maximizeButton.setLayerType(View.LAYER_TYPE_HARDWARE, null) - maximizeText.setLayerType(View.LAYER_TYPE_HARDWARE, null) + sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null) + sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null) menuAnimatorSet = AnimatorSet() menuAnimatorSet?.playTogether( ObjectAnimator.ofFloat(rootView, SCALE_Y, STARTING_MENU_HEIGHT_SCALE, 1f) @@ -388,9 +410,9 @@ class MaximizeMenu( // Scale up the children of the maximize menu so that the menu // scale is cancelled out and only the background is scaled. val value = animatedValue as Float - maximizeButton.scaleY = value + sizeToggleButton.scaleY = value snapButtonsLayout.scaleY = value - maximizeText.scaleY = value + sizeToggleButtonText.scaleY = value snapWindowText.scaleY = value } }, @@ -409,9 +431,9 @@ class MaximizeMenu( startDelay = CONTROLS_ALPHA_OPEN_MENU_ANIMATION_DELAY_MS addUpdateListener { val value = animatedValue as Float - maximizeButton.alpha = value + sizeToggleButton.alpha = value snapButtonsLayout.alpha = value - maximizeText.alpha = value + sizeToggleButtonText.alpha = value snapWindowText.alpha = value } }, @@ -423,8 +445,8 @@ class MaximizeMenu( ) menuAnimatorSet?.addListener( onEnd = { - maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null) - maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null) + sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null) + sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null) onEnd.invoke() } ) @@ -433,8 +455,8 @@ class MaximizeMenu( /** Animate the closing of the menu */ fun animateCloseMenu(onEnd: (() -> Unit)) { - maximizeButton.setLayerType(View.LAYER_TYPE_HARDWARE, null) - maximizeText.setLayerType(View.LAYER_TYPE_HARDWARE, null) + sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null) + sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null) cancelAnimation() menuAnimatorSet = AnimatorSet() menuAnimatorSet?.playTogether( @@ -464,9 +486,9 @@ class MaximizeMenu( // Scale up the children of the maximize menu so that the menu // scale is cancelled out and only the background is scaled. val value = animatedValue as Float - maximizeButton.scaleY = value + sizeToggleButton.scaleY = value snapButtonsLayout.scaleY = value - maximizeText.scaleY = value + sizeToggleButtonText.scaleY = value snapWindowText.scaleY = value } }, @@ -485,9 +507,9 @@ class MaximizeMenu( duration = ALPHA_ANIMATION_DURATION_MS addUpdateListener { val value = animatedValue as Float - maximizeButton.alpha = value + sizeToggleButton.alpha = value snapButtonsLayout.alpha = value - maximizeText.alpha = value + sizeToggleButtonText.alpha = value snapWindowText.alpha = value } }, @@ -498,8 +520,8 @@ class MaximizeMenu( ) menuAnimatorSet?.addListener( onEnd = { - maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null) - maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null) + sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null) + sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null) onEnd?.invoke() } ) @@ -509,8 +531,8 @@ class MaximizeMenu( /** Request that the accessibility service focus on the menu. */ fun requestAccessibilityFocus() { // Focus the first button in the menu by default. - maximizeButton.post { - maximizeButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + sizeToggleButton.post { + sizeToggleButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } } @@ -685,15 +707,31 @@ class MaximizeMenu( paint.color = strokeAndFillColor paint.style = Paint.Style.FILL }) + + val (horizontalFillPadding, verticalFillPadding) = + if (sizeToggleDirection == SizeToggleDirection.MAXIMIZE) { + context.resources.getDimensionPixelSize(R.dimen + .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding) to + context.resources.getDimensionPixelSize(R.dimen + .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding) + } else { + context.resources.getDimensionPixelSize(R.dimen + .desktop_mode_maximize_menu_restore_button_fill_horizontal_padding) to + context.resources.getDimensionPixelSize(R.dimen + .desktop_mode_maximize_menu_restore_button_fill_vertical_padding) + } + return LayerDrawable(layers.toTypedArray()).apply { when (numberOfLayers) { 3 -> { setLayerInset(1, outlineStroke) - setLayerInset(2, fillPadding) + setLayerInset(2, horizontalFillPadding, verticalFillPadding, + horizontalFillPadding, verticalFillPadding) } 4 -> { setLayerInset(intArrayOf(1, 2), outlineStroke) - setLayerInset(3, fillPadding) + setLayerInset(3, horizontalFillPadding, verticalFillPadding, + horizontalFillPadding, verticalFillPadding) } else -> error("Unexpected number of layers: $numberOfLayers") } @@ -737,6 +775,11 @@ class MaximizeMenu( enum class SnapToHalfSelection { NONE, LEFT, RIGHT } + + /** The possible selection states of the size toggle button in the maximize menu. */ + enum class SizeToggleDirection { + MAXIMIZE, RESTORE + } } companion object { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt index cf03b3f74dc7..d94391820d05 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt @@ -70,7 +70,7 @@ class AppHeaderViewHolder( rootView: View, onCaptionTouchListener: View.OnTouchListener, onCaptionButtonClickListener: View.OnClickListener, - onLongClickListener: OnLongClickListener, + private val onLongClickListener: OnLongClickListener, onCaptionGenericMotionListener: View.OnGenericMotionListener, appName: CharSequence, appIconBitmap: Bitmap, @@ -81,7 +81,8 @@ class AppHeaderViewHolder( val taskInfo: RunningTaskInfo, val isRequestingImmersive: Boolean, val inFullImmersiveState: Boolean, - val hasGlobalFocus: Boolean + val hasGlobalFocus: Boolean, + val enableMaximizeLongClick: Boolean, ) : Data() private val decorThemeUtil = DecorThemeUtil(context) @@ -160,19 +161,30 @@ class AppHeaderViewHolder( } override fun bindData(data: HeaderData) { - bindData(data.taskInfo, data.isRequestingImmersive, data.inFullImmersiveState, - data.hasGlobalFocus) + bindData( + data.taskInfo, + data.isRequestingImmersive, + data.inFullImmersiveState, + data.hasGlobalFocus, + data.enableMaximizeLongClick + ) } private fun bindData( taskInfo: RunningTaskInfo, isRequestingImmersive: Boolean, inFullImmersiveState: Boolean, - hasGlobalFocus: Boolean + hasGlobalFocus: Boolean, + enableMaximizeLongClick: Boolean, ) { if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) { - bindDataWithThemedHeaders(taskInfo, isRequestingImmersive, inFullImmersiveState, - hasGlobalFocus) + bindDataWithThemedHeaders( + taskInfo, + isRequestingImmersive, + inFullImmersiveState, + hasGlobalFocus, + enableMaximizeLongClick, + ) } else { bindDataLegacy(taskInfo, hasGlobalFocus) } @@ -215,7 +227,8 @@ class AppHeaderViewHolder( taskInfo: RunningTaskInfo, requestingImmersive: Boolean, inFullImmersiveState: Boolean, - hasGlobalFocus: Boolean + hasGlobalFocus: Boolean, + enableMaximizeLongClick: Boolean, ) { val header = fillHeaderInfo(taskInfo, hasGlobalFocus) val headerStyle = getHeaderStyle(header) @@ -281,6 +294,16 @@ class AppHeaderViewHolder( drawableInsets = closeDrawableInsets ) } + if (!enableMaximizeLongClick) { + maximizeButtonView.cancelHoverAnimation() + } + maximizeButtonView.hoverDisabled = !enableMaximizeLongClick + maximizeWindowButton.onLongClickListener = if (enableMaximizeLongClick) { + onLongClickListener + } else { + // Disable long-click to open maximize menu when in immersive. + null + } } override fun onHandleMenuOpened() {} @@ -291,14 +314,6 @@ class AppHeaderViewHolder( } } - fun setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition: Boolean) { - // If animating a task resize or reposition, cancel any running hover animations - if (animatingTaskResizeOrReposition) { - maximizeButtonView.cancelHoverAnimation() - } - maximizeButtonView.hoverDisabled = animatingTaskResizeOrReposition - } - fun onMaximizeWindowHoverExit() { maximizeButtonView.cancelHoverAnimation() } @@ -364,6 +379,11 @@ class AppHeaderViewHolder( private fun shouldShowExitFullImmersiveIcon( requestingImmersive: Boolean, inFullImmersiveState: Boolean + ): Boolean = isInFullImmersiveStateAndRequesting(requestingImmersive, inFullImmersiveState) + + private fun isInFullImmersiveStateAndRequesting( + requestingImmersive: Boolean, + inFullImmersiveState: Boolean ): Boolean = Flags.enableFullyImmersiveInDesktop() && requestingImmersive && inFullImmersiveState diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt index 7640cb1fb616..b7ddfd1fc6eb 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt @@ -154,6 +154,7 @@ class DesktopModeFlickerScenarios { TaggedCujTransitionMatcher(associatedTransitionRequired = false) ) .build(), + // TODO(373638597) Add AppLayerIncreasesInSize assertion assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS ) @@ -208,7 +209,7 @@ class DesktopModeFlickerScenarios { assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf( - AppLayerIncreasesInSize(DESKTOP_MODE_APP), + // TODO(373638597) Add AppLayerIncreasesInSize assertion AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP), AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP) ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt index 0b98ba2a9cd4..aa4f133123f5 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt @@ -24,7 +24,7 @@ import android.tools.flicker.config.FlickerConfig import android.tools.flicker.config.FlickerServiceConfig import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MAXIMUM_SIZE -import com.android.wm.shell.scenarios.ResizeAppWithCornerResize +import com.android.wm.shell.scenarios.MaximiseAppWithCornerResize import org.junit.Test import org.junit.runner.RunWith @@ -35,7 +35,7 @@ import org.junit.runner.RunWith * Assert that the maximum window size constraint is maintained. */ @RunWith(FlickerServiceJUnit4ClassRunner::class) -class ResizeAppToMaximumWindowSizeLandscape : ResizeAppWithCornerResize( +class ResizeAppToMaximumWindowSizeLandscape : MaximiseAppWithCornerResize( rotation = Rotation.ROTATION_90 ) { @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"]) diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt index b1c04d38a46c..e6b1ccf0111f 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt @@ -23,7 +23,7 @@ import android.tools.flicker.config.FlickerConfig import android.tools.flicker.config.FlickerServiceConfig import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MAXIMUM_SIZE -import com.android.wm.shell.scenarios.ResizeAppWithCornerResize +import com.android.wm.shell.scenarios.MaximiseAppWithCornerResize import org.junit.Test import org.junit.runner.RunWith @@ -34,7 +34,7 @@ import org.junit.runner.RunWith * Assert that the maximum window size constraint is maintained. */ @RunWith(FlickerServiceJUnit4ClassRunner::class) -class ResizeAppToMaximumWindowSizePortrait : ResizeAppWithCornerResize() { +class ResizeAppToMaximumWindowSizePortrait : MaximiseAppWithCornerResize() { @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"]) @Test override fun resizeAppWithCornerResizeToMaximumSize() = diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximiseAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximiseAppWithCornerResize.kt new file mode 100644 index 000000000000..6637b01f9d9c --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximiseAppWithCornerResize.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.scenarios + +import android.app.Instrumentation +import android.tools.NavBar +import android.tools.Rotation +import android.tools.flicker.rules.ChangeDisplayOrientationRule +import android.tools.traces.parsers.WindowManagerStateHelper +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper.AppProperty +import com.android.server.wm.flicker.helpers.NonResizeableAppHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.window.flags.Flags +import com.android.wm.shell.Utils +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test + +@Ignore("Test Base Class") +abstract class MaximiseAppWithCornerResize( + val rotation: Rotation = Rotation.ROTATION_0, + val appProperty: AppProperty = AppProperty.STANDARD +) { + + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val tapl = LauncherInstrumentation() + private val wmHelper = WindowManagerStateHelper(instrumentation) + private val device = UiDevice.getInstance(instrumentation) + private val maxResizeChange = 3000 + private val testApp = + DesktopModeAppHelper( + when (appProperty) { + AppProperty.STANDARD -> SimpleAppHelper(instrumentation) + AppProperty.NON_RESIZABLE -> NonResizeableAppHelper(instrumentation) + } + ) + + @Rule + @JvmField + val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation) + + @Before + fun setup() { + Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + tapl.setEnableRotation(true) + tapl.setExpectedRotation(rotation.value) + ChangeDisplayOrientationRule.setRotation(rotation) + testApp.enterDesktopWithDrag(wmHelper, device) + testApp.cornerResize( + wmHelper, + device, + DesktopModeAppHelper.Corners.RIGHT_TOP, + maxResizeChange, + -maxResizeChange + ) + } + + @Test + open fun resizeAppWithCornerResizeToMaximumSize() { + testApp.cornerResize( + wmHelper, + device, + DesktopModeAppHelper.Corners.LEFT_BOTTOM, + -maxResizeChange, + maxResizeChange + ) + } + + @After + fun teardown() { + testApp.exit(wmHelper) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt index bd25639466a3..a7cebf402d8e 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt @@ -19,11 +19,13 @@ package com.android.wm.shell.scenarios import android.app.Instrumentation import android.tools.NavBar import android.tools.Rotation +import android.tools.flicker.rules.ChangeDisplayOrientationRule import android.tools.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper.AppProperty import com.android.server.wm.flicker.helpers.NonResizeableAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.window.flags.Flags @@ -63,6 +65,7 @@ abstract class ResizeAppWithCornerResize( fun setup() { Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) tapl.setEnableRotation(true) + ChangeDisplayOrientationRule.setRotation(rotation) tapl.setExpectedRotation(rotation.value) testApp.enterDesktopWithDrag(wmHelper, device) } @@ -78,34 +81,8 @@ abstract class ResizeAppWithCornerResize( ) } - @Test - open fun resizeAppWithCornerResizeToMaximumSize() { - val maxResizeChange = 3000 - testApp.cornerResize( - wmHelper, - device, - DesktopModeAppHelper.Corners.RIGHT_TOP, - maxResizeChange, - -maxResizeChange - ) - testApp.cornerResize( - wmHelper, - device, - DesktopModeAppHelper.Corners.LEFT_BOTTOM, - -maxResizeChange, - maxResizeChange - ) - } - @After fun teardown() { testApp.exit(wmHelper) } - - companion object { - enum class AppProperty { - STANDARD, - NON_RESIZABLE - } - } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java index 094af9652ea3..a1d4a1a301bd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java @@ -26,6 +26,7 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; +import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.wm.shell.ShellTestCase; import org.junit.Before; @@ -45,6 +46,7 @@ public class BubbleOverflowTest extends ShellTestCase { private TestableBubblePositioner mPositioner; private BubbleOverflow mOverflow; private BubbleExpandedViewManager mExpandedViewManager; + private BubbleLogger mBubbleLogger; @Mock private BubbleController mBubbleController; @@ -58,6 +60,7 @@ public class BubbleOverflowTest extends ShellTestCase { mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(mBubbleController); mPositioner = new TestableBubblePositioner(mContext, mContext.getSystemService(WindowManager.class)); + mBubbleLogger = new BubbleLogger(new UiEventLoggerFake()); when(mBubbleController.getPositioner()).thenReturn(mPositioner); when(mBubbleController.getStackView()).thenReturn(mBubbleStackView); @@ -77,7 +80,7 @@ public class BubbleOverflowTest extends ShellTestCase { @Test public void test_initialize_forBubbleBar() { - mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner); + mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner, mBubbleLogger); assertThat(mOverflow.getBubbleBarExpandedView()).isNotNull(); assertThat(mOverflow.getExpandedView()).isNull(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt index 2e9effb44d67..b13746814f58 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt @@ -16,12 +16,15 @@ package com.android.wm.shell.desktopmode import android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS +import android.graphics.Rect import android.os.Binder import android.os.IBinder +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY +import android.view.Surface import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TransitionFlags @@ -68,6 +71,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { private lateinit var desktopRepository: DesktopRepository @Mock private lateinit var mockDisplayController: DisplayController @Mock private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer + @Mock private lateinit var mockDisplayLayout: DisplayLayout private val transactionSupplier = { SurfaceControl.Transaction() } private lateinit var immersiveHandler: DesktopFullImmersiveTransitionHandler @@ -78,7 +82,10 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { context, ShellInit(TestShellExecutor()), mock(), mock() ) whenever(mockDisplayController.getDisplayLayout(DEFAULT_DISPLAY)) - .thenReturn(DisplayLayout()) + .thenReturn(mockDisplayLayout) + whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { invocation -> + (invocation.getArgument(0) as Rect).set(STABLE_BOUNDS) + } immersiveHandler = DesktopFullImmersiveTransitionHandler( transitions = mockTransitions, desktopRepository = desktopRepository, @@ -101,12 +108,50 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { ) immersiveHandler.moveTaskToImmersive(task) - immersiveHandler.onTransitionReady(mockBinder, createTransitionInfo()) + immersiveHandler.onTransitionReady( + transition = mockBinder, + info = createTransitionInfo( + changes = listOf( + TransitionInfo.Change(task.token, SurfaceControl()).apply { + taskInfo = task + } + ) + ) + ) assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isTrue() } @Test + @EnableFlags(Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE) + fun enterImmersive_savesPreImmersiveBounds() { + val task = createFreeformTask() + val mockBinder = mock(IBinder::class.java) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + .thenReturn(mockBinder) + desktopRepository.setTaskInFullImmersiveState( + displayId = task.displayId, + taskId = task.taskId, + immersive = false + ) + assertThat(desktopRepository.removeBoundsBeforeFullImmersive(task.taskId)).isNull() + + immersiveHandler.moveTaskToImmersive(task) + immersiveHandler.onTransitionReady( + transition = mockBinder, + info = createTransitionInfo( + changes = listOf( + TransitionInfo.Change(task.token, SurfaceControl()).apply { + taskInfo = task + } + ) + ) + ) + + assertThat(desktopRepository.removeBoundsBeforeFullImmersive(task.taskId)).isNotNull() + } + + @Test fun exitImmersive_transitionReady_updatesRepository() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) @@ -119,7 +164,69 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { ) immersiveHandler.moveTaskToNonImmersive(task) - immersiveHandler.onTransitionReady(mockBinder, createTransitionInfo()) + immersiveHandler.onTransitionReady( + transition = mockBinder, + info = createTransitionInfo( + changes = listOf( + TransitionInfo.Change(task.token, SurfaceControl()).apply { + taskInfo = task + } + ) + ) + ) + + assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE) + fun exitImmersive_onTransitionReady_removesBoundsBeforeImmersive() { + val task = createFreeformTask() + val mockBinder = mock(IBinder::class.java) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + .thenReturn(mockBinder) + desktopRepository.setTaskInFullImmersiveState( + displayId = task.displayId, + taskId = task.taskId, + immersive = true + ) + desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600)) + + immersiveHandler.moveTaskToNonImmersive(task) + immersiveHandler.onTransitionReady( + transition = mockBinder, + info = createTransitionInfo( + changes = listOf( + TransitionInfo.Change(task.token, SurfaceControl()).apply { + taskInfo = task + } + ) + ) + ) + + assertThat(desktopRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() + } + + @Test + fun onTransitionReady_displayRotation_exitsImmersive() { + val task = createFreeformTask() + desktopRepository.setTaskInFullImmersiveState( + displayId = task.displayId, + taskId = task.taskId, + immersive = true + ) + + immersiveHandler.onTransitionReady( + transition = mock(IBinder::class.java), + info = createTransitionInfo( + changes = listOf( + TransitionInfo.Change(task.token, SurfaceControl()).apply { + taskInfo = task + setRotation(/* start= */ Surface.ROTATION_0, /* end= */ Surface.ROTATION_90) + } + ) + ) + ) assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse() } @@ -361,6 +468,103 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse() } + @Test + @EnableFlags( + Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP, + Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE + ) + fun onTransitionReady_pendingExit_removesBoundsBeforeImmersive() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + val transition = Binder() + desktopRepository.setTaskInFullImmersiveState( + displayId = DEFAULT_DISPLAY, + taskId = task.taskId, + immersive = true + ) + desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600)) + immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + + immersiveHandler.onTransitionReady( + transition = transition, + info = createTransitionInfo( + changes = listOf( + TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task } + ) + ) + ) + + assertThat(desktopRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + @DisableFlags(Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE) + fun exitImmersiveIfApplicable_changesBoundsToMaximize() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + desktopRepository.setTaskInFullImmersiveState( + displayId = DEFAULT_DISPLAY, + taskId = task.taskId, + immersive = true + ) + + immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + + assertThat( + wct.hasBoundsChange(task.token, calculateMaximizeBounds(mockDisplayLayout, task)) + ).isTrue() + } + + @Test + @EnableFlags( + Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP, + Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE + ) + fun exitImmersiveIfApplicable_preImmersiveBoundsSaved_changesBoundsToPreImmersiveBounds() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + desktopRepository.setTaskInFullImmersiveState( + displayId = DEFAULT_DISPLAY, + taskId = task.taskId, + immersive = true + ) + val preImmersiveBounds = Rect(100, 100, 500, 500) + desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, preImmersiveBounds) + + immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + + assertThat( + wct.hasBoundsChange(task.token, preImmersiveBounds) + ).isTrue() + } + + @Test + @EnableFlags( + Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP, + Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE, + Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS + ) + fun exitImmersiveIfApplicable_preImmersiveBoundsNotSaved_changesBoundsToInitialBounds() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + desktopRepository.setTaskInFullImmersiveState( + displayId = DEFAULT_DISPLAY, + taskId = task.taskId, + immersive = true + ) + + immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + + assertThat( + wct.hasBoundsChange(task.token, calculateInitialBounds(mockDisplayLayout, task)) + ).isTrue() + } + private fun createTransitionInfo( @TransitionType type: Int = TRANSIT_CHANGE, @TransitionFlags flags: Int = 0, @@ -374,4 +578,17 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { change.key == token.asBinder() && (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0 } + + private fun WindowContainerTransaction.hasBoundsChange( + token: WindowContainerToken, + bounds: Rect, + ): Boolean = this.changes.any { change -> + change.key == token.asBinder() + && (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0 + && change.value.configuration.windowConfiguration.bounds == bounds + } + + companion object { + private val STABLE_BOUNDS = Rect(0, 100, 2000, 1900) + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt index dde9fda13ea9..0825b6b0d7be 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt @@ -18,7 +18,10 @@ package com.android.wm.shell.desktopmode import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule +import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations +import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker import com.android.dx.mockito.inline.extended.ExtendedMockito.verify +import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions import com.android.internal.util.FrameworkStatsLog import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.window.flags.Flags @@ -26,15 +29,16 @@ import com.android.wm.shell.EventLogTags import com.android.wm.shell.ShellTestCase import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.NO_SESSION_ID import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger -import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod -import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskSizeUpdate -import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_MINIMIZE_REASON import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON -import kotlinx.coroutines.runBlocking +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason +import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test import org.mockito.kotlin.eq @@ -49,40 +53,87 @@ class DesktopModeEventLoggerTest : ShellTestCase() { @JvmField @Rule(order = 0) val extendedMockitoRule = ExtendedMockitoRule.Builder(this) - .mockStatic(FrameworkStatsLog::class.java) - .mockStatic(EventLogTags::class.java).build()!! + .mockStatic(FrameworkStatsLog::class.java) + .mockStatic(EventLogTags::class.java).build()!! @JvmField @Rule(order = 1) val setFlagsRule = SetFlagsRule() @Test - fun logSessionEnter_enterReason() = runBlocking { - desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER) + fun logSessionEnter_logsEnterReasonWithNewSessionId() { + desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) + + val sessionId = desktopModeEventLogger.currentSessionId.get() + assertThat(sessionId).isNotEqualTo(NO_SESSION_ID) + verify { + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), + /* event */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER), + /* enter_reason */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER), + /* exit_reason */ + eq(0), + /* sessionId */ + eq(sessionId) + ) + } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verify { + EventLogTags.writeWmShellEnterDesktopMode( + eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason), + eq(sessionId) + ) + } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logSessionEnter_ongoingSession_logsEnterReasonWithNewSessionId() { + val previousSessionId = startDesktopModeSession() + desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) + + val sessionId = desktopModeEventLogger.currentSessionId.get() + assertThat(sessionId).isNotEqualTo(NO_SESSION_ID) + assertThat(sessionId).isNotEqualTo(previousSessionId) verify { FrameworkStatsLog.write( eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), /* event */ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER), /* enter_reason */ - eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER), + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER), /* exit_reason */ eq(0), /* sessionId */ - eq(SESSION_ID) + eq(sessionId) ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellEnterDesktopMode( - eq(EnterReason.UNKNOWN_ENTER.reason), - eq(SESSION_ID)) + eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason), + eq(sessionId) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logSessionExit_exitReason() = runBlocking { - desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT) + fun logSessionExit_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logSessionExit_logsExitReasonAndClearsSessionId() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT) verify { FrameworkStatsLog.write( @@ -92,24 +143,39 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* enter_reason */ eq(0), /* exit_reason */ - eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT), + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT), /* sessionId */ - eq(SESSION_ID) + eq(sessionId) ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellExitDesktopMode( - eq(ExitReason.UNKNOWN_EXIT.reason), - eq(SESSION_ID)) + eq(ExitReason.DRAG_TO_EXIT.reason), + eq(sessionId) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID) + } + + @Test + fun logTaskAdded_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskAdded(TASK_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskAdded_taskUpdate() = runBlocking { - desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE) + fun logTaskAdded_logsTaskUpdate() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskAdded(TASK_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED), /* instance_id */ @@ -125,36 +191,52 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logTaskRemoved_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskRemoved(TASK_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskRemoved_taskUpdate() = runBlocking { - desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE) + fun logTaskRemoved_taskUpdate() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskRemoved(TASK_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED), /* instance_id */ @@ -170,39 +252,57 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logTaskInfoChanged_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskInfoChanged_taskUpdate() = runBlocking { - desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE) + fun logTaskInfoChanged_taskUpdate() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), /* instance_id */ eq(TASK_UPDATE.instanceId), /* uid */ @@ -216,40 +316,51 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() = runBlocking { - desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, - createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT)) + fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskInfoChanged( + createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT) + ) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), /* instance_id */ eq(TASK_UPDATE.instanceId), /* uid */ @@ -263,42 +374,53 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), /* minimize_reason */ eq(MinimizeReason.TASK_LIMIT.reason), /* unminimize_reason */ eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(MinimizeReason.TASK_LIMIT.reason), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() = runBlocking { - desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, - createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP)) + fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskInfoChanged( + createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP) + ) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), /* instance_id */ eq(TASK_UPDATE.instanceId), /* uid */ @@ -312,39 +434,55 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), /* minimize_reason */ eq(UNSET_MINIMIZE_REASON), /* unminimize_reason */ eq(UnminimizeReason.TASKBAR_TAP.reason), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UnminimizeReason.TASKBAR_TAP.reason), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logTaskResizingStarted_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS) - fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() = runBlocking { - desktopModeEventLogger.logTaskResizingStarted(sessionId = SESSION_ID, createTaskSizeUpdate()) + fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), /* resize_trigger */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER), /* resizing_stage */ @@ -352,7 +490,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* input_method */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD), /* desktop_mode_session_id */ - eq(SESSION_ID), + eq(sessionId), /* instance_id */ eq(TASK_SIZE_UPDATE.instanceId), /* uid */ @@ -365,15 +503,27 @@ class DesktopModeEventLoggerTest : ShellTestCase() { eq(TASK_SIZE_UPDATE.displayArea), ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + } + + @Test + fun logTaskResizingEnded_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS) - fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() = runBlocking { - desktopModeEventLogger.logTaskResizingEnded(sessionId = SESSION_ID, createTaskSizeUpdate()) + fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), /* resize_trigger */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER), /* resizing_stage */ @@ -381,7 +531,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* input_method */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD), /* desktop_mode_session_id */ - eq(SESSION_ID), + eq(sessionId), /* instance_id */ eq(TASK_SIZE_UPDATE.instanceId), /* uid */ @@ -394,6 +544,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() { eq(TASK_SIZE_UPDATE.displayArea), ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + } + + private fun startDesktopModeSession(): Int { + desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) + clearInvocations(staticMockMarker(FrameworkStatsLog::class.java)) + clearInvocations(staticMockMarker(EventLogTags::class.java)) + return desktopModeEventLogger.currentSessionId.get() } @Test @@ -428,7 +586,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { } private companion object { - private const val SESSION_ID = 1 + private const val sessionId = 1 private const val TASK_ID = 1 private const val TASK_UID = 1 private const val TASK_X = 0 @@ -453,23 +611,12 @@ class DesktopModeEventLoggerTest : ShellTestCase() { DISPLAY_AREA, ) - private fun createTaskSizeUpdate( - resizeTrigger: ResizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, - inputMethod: InputMethod = InputMethod.UNKNOWN_INPUT_METHOD, - ) = TaskSizeUpdate( - resizeTrigger, - inputMethod, - TASK_ID, - TASK_UID, - TASK_HEIGHT, - TASK_WIDTH, - DISPLAY_AREA, - ) - private fun createTaskUpdate( minimizeReason: MinimizeReason? = null, unminimizeReason: UnminimizeReason? = null, - ) = TaskUpdate(TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason, - unminimizeReason, TASK_COUNT) + ) = TaskUpdate( + TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason, + unminimizeReason, TASK_COUNT + ) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt index e7593b5b9324..f25faa53f356 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt @@ -59,8 +59,8 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.TransitionInfoBuilder import com.android.wm.shell.transition.Transitions -import junit.framework.Assert.assertNotNull -import junit.framework.Assert.assertNull +import kotlin.test.assertFalse +import kotlin.test.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test @@ -142,8 +142,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) - verify(desktopModeEventLogger, never()).logTaskAdded(any(), any()) + verify(desktopModeEventLogger, never()).logSessionEnter(any()) + verify(desktopModeEventLogger, never()).logTaskAdded(any()) } @Test @@ -228,11 +228,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() { // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -241,7 +240,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // Enter desktop mode from cancelled recents has no transition. Enter is detected on the // next transition involving freeform windows @@ -259,11 +259,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() { // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -272,7 +271,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // Enter desktop mode from cancelled recents has no transition. Enter is detected on the // next transition involving freeform windows @@ -290,11 +290,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() { // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -303,7 +302,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // Enter desktop mode from cancelled recents has no transition. Enter is detected on the // next transition involving freeform windows @@ -324,11 +324,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { // Tests for AppFromOverview precedence in compared to cancelled Overview // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -337,7 +336,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM)) @@ -379,11 +379,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() { val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM) // Previous Exit reason recorded as Screen Off - val sessionId = 1 transitionObserver.addTaskInfosToCachedMap(freeformTask) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build()) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) // Enter desktop through back transition, this happens when user enters after dismissing // keyguard val change = createChange(TRANSIT_TO_FRONT, freeformTask) @@ -399,11 +398,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() { val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM) // Previous Exit reason recorded as Screen Off - val sessionId = 1 transitionObserver.addTaskInfosToCachedMap(freeformTask) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build()) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) // Enter desktop through app handle drag. This represents cases where instead of moving to // desktop right after turning the screen on, we move to fullscreen then move another task @@ -419,24 +417,22 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { } @Test - fun transitSleep_logTaskRemovedAndExitReasonScreenOff_sessionIdNull() { - val sessionId = 1 + fun transitSleep_logTaskRemovedAndExitReasonScreenOff() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -444,15 +440,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -462,16 +457,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { .build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -479,16 +472,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -496,15 +487,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() { - val sessionId = 1 + fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // recents transition val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM)) @@ -513,31 +503,30 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) } @Test - fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() { - val sessionId = 1 + fun transitClose_logTaskRemovedAndExitReasonTaskFinished() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task closing val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE) } @Test fun sessionExitByRecents_cancelledAnimation_sessionRestored() { - val sessionId = 1 // add a freeform task to an existing session val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(taskInfo) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // recents transition sent freeform window to back val change = createChange(TRANSIT_TO_BACK, taskInfo) @@ -546,7 +535,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo1) verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build() callOnTransitionReady(transitionInfo2) @@ -557,10 +547,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() { - val sessionId = 1 // add an existing freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // new freeform task added val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)) @@ -568,18 +557,16 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verify(desktopModeEventLogger, times(1)) - .logTaskAdded(eq(sessionId), - eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2))) - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) + .logTaskAdded(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2))) + verify(desktopModeEventLogger, never()).logSessionEnter(any()) } @Test fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() { - val sessionId = 1 // add an existing freeform task val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(taskInfo) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task position changed val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100) @@ -591,18 +578,17 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), - eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))) + eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1)) + ) verifyZeroInteractions(desktopModeEventLogger) } @Test fun sessionAlreadyStarted_taskResized_logsTaskUpdate() { - val sessionId = 1 // add an existing freeform task val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(taskInfo) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task resized val newTaskInfo = @@ -618,23 +604,22 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq( DEFAULT_TASK_UPDATE.copy( taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100, - visibleTaskCount = 1))) + visibleTaskCount = 1)) + ) verifyZeroInteractions(desktopModeEventLogger) } @Test fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() { - val sessionId = 1 // add 2 existing freeform task val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM) val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2) transitionObserver.addTaskInfosToCachedMap(taskInfo1) transitionObserver.addTaskInfosToCachedMap(taskInfo2) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task 1 position update val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100) @@ -646,8 +631,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy( - taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))) + eq(DEFAULT_TASK_UPDATE.copy( + taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2)) + ) verifyZeroInteractions(desktopModeEventLogger) // task 2 resize @@ -666,7 +652,6 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq( DEFAULT_TASK_UPDATE.copy( instanceId = 2, @@ -679,11 +664,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() { - val sessionId = 1 // add two existing freeform tasks transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // new freeform task closed val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)) @@ -691,9 +675,11 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verify(desktopModeEventLogger, times(1)) - .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy( - instanceId = 2, visibleTaskCount = 1))) - verify(desktopModeEventLogger, never()).logSessionExit(any(), any()) + .logTaskRemoved( + eq(DEFAULT_TASK_UPDATE.copy( + instanceId = 2, visibleTaskCount = 1)) + ) + verify(desktopModeEventLogger, never()).logSessionExit(any()) } /** Simulate calling the onTransitionReady() method */ @@ -706,10 +692,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { } private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) { - val sessionId = transitionObserver.getLoggerSessionId() - assertNotNull(sessionId) - verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate)) + assertTrue(transitionObserver.isSessionActive) + verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(enterReason)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(taskUpdate)) ExtendedMockito.verify { Trace.setCounter( eq(Trace.TRACE_TAG_WINDOW_MANAGER), @@ -725,14 +710,13 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { } private fun verifyTaskRemovedAndExitLogging( - sessionId: Int, exitReason: ExitReason, taskUpdate: TaskUpdate ) { - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), eq(taskUpdate)) - verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), eq(exitReason)) + assertFalse(transitionObserver.isSessionActive) + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate)) + verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason)) verifyZeroInteractions(desktopModeEventLogger) - assertNull(transitionObserver.getLoggerSessionId()) } private companion object { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt index e20f0ecb1f3b..3e2280393c2b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt @@ -746,6 +746,18 @@ class DesktopRepositoryTest : ShellTestCase() { } @Test + fun removeFreeformTask_removesTaskBoundsBeforeImmersive() { + val taskId = 1 + repo.addActiveTask(THIRD_DISPLAY, taskId) + repo.addOrMoveFreeformTaskToTop(THIRD_DISPLAY, taskId) + repo.saveBoundsBeforeFullImmersive(taskId, Rect(0, 0, 200, 200)) + + repo.removeFreeformTask(THIRD_DISPLAY, taskId) + + assertThat(repo.removeBoundsBeforeFullImmersive(taskId)).isNull() + } + + @Test fun removeFreeformTask_removesActiveTask() { val taskId = 1 val listener = TestListener() @@ -805,6 +817,28 @@ class DesktopRepositoryTest : ShellTestCase() { } @Test + fun saveBoundsBeforeImmersive_boundsSavedByTaskId() { + val taskId = 1 + val bounds = Rect(0, 0, 200, 200) + + repo.saveBoundsBeforeFullImmersive(taskId, bounds) + + assertThat(repo.removeBoundsBeforeFullImmersive(taskId)).isEqualTo(bounds) + } + + @Test + fun removeBoundsBeforeImmersive_returnsNullAfterBoundsRemoved() { + val taskId = 1 + val bounds = Rect(0, 0, 200, 200) + repo.saveBoundsBeforeFullImmersive(taskId, bounds) + repo.removeBoundsBeforeFullImmersive(taskId) + + val boundsBeforeImmersive = repo.removeBoundsBeforeFullImmersive(taskId) + + assertThat(boundsBeforeImmersive).isNull() + } + + @Test fun isMinimizedTask_minimizeTaskNotCalled_noTasksMinimized() { assertThat(repo.isMinimizedTask(taskId = 0)).isFalse() assertThat(repo.isMinimizedTask(taskId = 1)).isFalse() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index b3c10d64c3a3..113990e9d7d2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -97,6 +97,7 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreef import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask +import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler import com.android.wm.shell.desktopmode.persistence.Desktop import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository import com.android.wm.shell.draganddrop.DragAndDropController @@ -122,6 +123,7 @@ import java.util.function.Consumer import java.util.Optional import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue +import kotlin.test.assertIs import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlinx.coroutines.CoroutineScope @@ -1338,7 +1340,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task1 = setUpFreeformTask() setUpFreeformTask() - controller.moveTaskToFront(task1) + controller.moveTaskToFront(task1, remoteTransition = null) val wct = getLatestWct(type = TRANSIT_TO_FRONT) assertThat(wct.hierarchyOps).hasSize(1) @@ -1350,7 +1352,7 @@ class DesktopTasksControllerTest : ShellTestCase() { setUpHomeTask() val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() } - controller.moveTaskToFront(freeformTasks[0]) + controller.moveTaskToFront(freeformTasks[0], remoteTransition = null) val wct = getLatestWct(type = TRANSIT_TO_FRONT) assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize @@ -1359,11 +1361,40 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun moveTaskToFront_remoteTransition_usesOneshotHandler() { + setUpHomeTask() + val freeformTasks = List(MAX_TASK_LIMIT) { setUpFreeformTask() } + val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java) + whenever( + transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()) + ).thenReturn(Binder()) + + controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition())) + + assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value) + } + + @Test + fun moveTaskToFront_bringsTasksOverLimit_remoteTransition_usesWindowLimitHandler() { + setUpHomeTask() + val freeformTasks = List(MAX_TASK_LIMIT + 1) { setUpFreeformTask() } + val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java) + whenever( + transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()) + ).thenReturn(Binder()) + + controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition())) + + assertThat(transitionHandlerArgCaptor.value) + .isInstanceOf(DesktopWindowLimitRemoteHandler::class.java) + } + + @Test fun moveTaskToFront_backgroundTask_launchesTask() { val task = createTaskInfo(1) whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null) - controller.moveTaskToFront(task.taskId) + controller.moveTaskToFront(task.taskId, remoteTransition = null) val wct = getLatestWct(type = TRANSIT_OPEN) assertThat(wct.hierarchyOps).hasSize(1) @@ -1376,12 +1407,12 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = createTaskInfo(1001) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null) - controller.moveTaskToFront(task.taskId) + controller.moveTaskToFront(task.taskId, remoteTransition = null) val wct = getLatestWct(type = TRANSIT_OPEN) assertThat(wct.hierarchyOps.size).isEqualTo(2) // launch + minimize - wct.assertReorderAt(0, freeformTasks[0], toTop = false) - wct.assertLaunchTaskAt(1, task.taskId, WINDOWING_MODE_FREEFORM) + wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM) + wct.assertReorderAt(1, freeformTasks[0], toTop = false) } @Test @@ -2902,7 +2933,8 @@ class DesktopTasksControllerTest : ShellTestCase() { runOpenNewWindow(task) verify(splitScreenController) .startIntent(any(), anyInt(), any(), any(), - optionsCaptor.capture(), anyOrNull()) + optionsCaptor.capture(), anyOrNull(), eq(true) + ) assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode) .isEqualTo(WINDOWING_MODE_MULTI_WINDOW) } @@ -2917,7 +2949,7 @@ class DesktopTasksControllerTest : ShellTestCase() { verify(splitScreenController) .startIntent( any(), anyInt(), any(), any(), - optionsCaptor.capture(), anyOrNull() + optionsCaptor.capture(), anyOrNull(), eq(true) ) assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode) .isEqualTo(WINDOWING_MODE_MULTI_WINDOW) @@ -2984,6 +3016,58 @@ class DesktopTasksControllerTest : ShellTestCase() { .launchWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM) } + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES) + fun openInstance_fromFreeform_minimizesIfNeeded() { + setUpLandscapeDisplay() + val homeTask = setUpHomeTask() + val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() } + val oldestTask = freeformTasks.first() + val newestTask = freeformTasks.last() + + runOpenInstance(newestTask, freeformTasks[1].taskId) + + val wct = getLatestWct(type = TRANSIT_OPEN) + // Home is moved to front of everything. + assertThat( + wct.hierarchyOps.any { hop -> + hop.container == homeTask.token.asBinder() && hop.toTop + } + ).isTrue() + // And the oldest task isn't moved in front of home, effectively minimizing it. + assertThat( + wct.hierarchyOps.none { hop -> + hop.container == oldestTask.token.asBinder() && hop.toTop + } + ).isTrue() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES) + fun openInstance_fromFreeform_exitsImmersiveIfNeeded() { + setUpLandscapeDisplay() + val homeTask = setUpHomeTask() + val freeformTask = setUpFreeformTask() + val immersiveTask = setUpFreeformTask() + taskRepository.setTaskInFullImmersiveState( + displayId = immersiveTask.displayId, + taskId = immersiveTask.taskId, + immersive = true + ) + val runOnStartTransit = RunOnStartTransitionCallback() + val transition = Binder() + whenever(transitions.startTransition(eq(TRANSIT_OPEN), any(), anyOrNull())) + .thenReturn(transition) + whenever(mockDesktopFullImmersiveTransitionHandler + .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))).thenReturn(runOnStartTransit) + + runOpenInstance(immersiveTask, freeformTask.taskId) + + verify(mockDesktopFullImmersiveTransitionHandler) + .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId)) + runOnStartTransit.assertOnlyInvocation(transition) + } + private fun runOpenInstance( callingTask: RunningTaskInfo, requestedTaskId: Int @@ -3319,7 +3403,7 @@ class DesktopTasksControllerTest : ShellTestCase() { .exitImmersiveIfApplicable(any(), eq(task.displayId))).thenReturn(runOnStartTransit) whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition) - controller.moveTaskToFront(task.taskId) + controller.moveTaskToFront(task.taskId, remoteTransition = null) verify(mockDesktopFullImmersiveTransitionHandler) .exitImmersiveIfApplicable(any(), eq(task.displayId)) @@ -3335,7 +3419,7 @@ class DesktopTasksControllerTest : ShellTestCase() { .exitImmersiveIfApplicable(any(), eq(task.displayId))).thenReturn(runOnStartTransit) whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition) - controller.moveTaskToFront(task.taskId) + controller.moveTaskToFront(task.taskId, remoteTransition = null) verify(mockDesktopFullImmersiveTransitionHandler) .exitImmersiveIfApplicable(any(), eq(task.displayId)) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt new file mode 100644 index 000000000000..6a5d9f67e4a7 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode.minimize + +import android.app.ActivityManager +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.os.Binder +import android.os.IBinder +import android.testing.AndroidTestingRunner +import android.view.SurfaceControl.Transaction +import android.view.WindowManager.TRANSIT_TO_BACK +import android.view.WindowManager.TRANSIT_TO_FRONT +import android.window.IRemoteTransition +import android.window.RemoteTransition +import androidx.test.filters.SmallTest +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.TestRunningTaskInfoBuilder +import com.android.wm.shell.TestShellExecutor +import com.android.wm.shell.transition.TransitionInfoBuilder +import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class DesktopWindowLimitRemoteHandlerTest { + + private val shellExecutor = TestShellExecutor() + private val transition: IBinder = Binder() + + private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>() + private val remoteTransition = mock<RemoteTransition>() + private val iRemoteTransition = mock<IRemoteTransition>() + private val startT = mock<Transaction>() + private val finishT = mock<Transaction>() + private val finishCallback = mock<Transitions.TransitionFinishCallback>() + + @Before + fun setUp() { + whenever(remoteTransition.remoteTransition).thenReturn(iRemoteTransition) + whenever(iRemoteTransition.asBinder()).thenReturn(mock(IBinder::class.java)) + } + + private fun createRemoteHandler(taskIdToMinimize: Int) = + DesktopWindowLimitRemoteHandler( + shellExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize) + + @Test + fun startAnimation_dontSetTransition_returnsFalse() { + val minimizeTask = createDesktopTask() + val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId) + + assertThat(remoteHandler.startAnimation(transition, + createMinimizeTransitionInfo(minimizeTask), startT, finishT, finishCallback) + ).isFalse() + } + + @Test + fun startAnimation_noMinimizeChange_returnsFalse() { + val remoteHandler = createRemoteHandler(taskIdToMinimize = 1) + remoteHandler.setTransition(transition) + val info = createToFrontTransitionInfo() + + assertThat( + remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback) + ).isFalse() + } + + @Test + fun startAnimation_correctTransition_returnsTrue() { + val minimizeTask = createDesktopTask() + val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId) + remoteHandler.setTransition(transition) + val info = createMinimizeTransitionInfo(minimizeTask) + + assertThat( + remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback) + ).isTrue() + } + + @Test + fun startAnimation_noMinimizeChange_doesNotReparentMinimizeChange() { + val remoteHandler = createRemoteHandler(taskIdToMinimize = 1) + remoteHandler.setTransition(transition) + val info = createToFrontTransitionInfo() + + remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback) + + verify(rootTaskDisplayAreaOrganizer, times(0)) + .reparentToDisplayArea(anyInt(), any(), any()) + } + + @Test + fun startAnimation_hasMinimizeChange_reparentsMinimizeChange() { + val minimizeTask = createDesktopTask() + val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId) + remoteHandler.setTransition(transition) + val info = createMinimizeTransitionInfo(minimizeTask) + + remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback) + + verify(rootTaskDisplayAreaOrganizer).reparentToDisplayArea(anyInt(), any(), any()) + } + + @Test + fun startAnimation_noMinimizeChange_doesNotStartRemoteAnimation() { + val minimizeTask = createDesktopTask() + val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId) + remoteHandler.setTransition(transition) + val info = createToFrontTransitionInfo() + + remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback) + + verify(iRemoteTransition, times(0)).startAnimation(any(), any(), any(), any()) + } + + @Test + fun startAnimation_hasMinimizeChange_startsRemoteAnimation() { + val minimizeTask = createDesktopTask() + val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId) + remoteHandler.setTransition(transition) + val info = createMinimizeTransitionInfo(minimizeTask) + + remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback) + + verify(iRemoteTransition).startAnimation(any(), any(), any(), any()) + } + + private fun createDesktopTask() = + TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build() + + private fun createToFrontTransitionInfo() = + TransitionInfoBuilder(TRANSIT_TO_FRONT) + .addChange(TRANSIT_TO_FRONT, + TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build()) + .build() + + private fun createMinimizeTransitionInfo(minimizeTask: ActivityManager.RunningTaskInfo) = + TransitionInfoBuilder(TRANSIT_TO_FRONT) + .addChange(TRANSIT_TO_FRONT, + TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build()) + .addChange(TRANSIT_TO_BACK, minimizeTask) + .build() +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java index 9260a07fd945..ef3af8e7bdac 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java @@ -297,6 +297,28 @@ public class SplitScreenControllerTests extends ShellTestCase { } @Test + public void startIntent_forceLaunchNewTaskTrue_skipsBackgroundTasks() { + Intent startIntent = createStartIntent("startActivity"); + PendingIntent pendingIntent = + PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE); + mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */, + true /* forceLaunchNewTask */); + verify(mRecentTasks, never()).findTaskInBackground(any(), anyInt(), any()); + } + + @Test + public void startIntent_forceLaunchNewTaskFalse_checksBackgroundTasks() { + Intent startIntent = createStartIntent("startActivity"); + PendingIntent pendingIntent = + PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE); + mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */, + false /* forceLaunchNewTask */); + verify(mRecentTasks).findTaskInBackground(any(), anyInt(), any()); + } + + @Test public void testSwitchSplitPosition_checksIsSplitScreenVisible() { final String reason = "test"; when(mSplitScreenController.isSplitScreenVisible()).thenReturn(true, false); diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 385fbfe1a86a..a39f30bbad1f 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -75,7 +75,6 @@ cc_library { "BigBufferStream.cpp", "ChunkIterator.cpp", "ConfigDescription.cpp", - "CursorWindow.cpp", "FileStream.cpp", "Idmap.cpp", "LoadedArsc.cpp", @@ -114,6 +113,7 @@ cc_library { srcs: [ "BackupData.cpp", "BackupHelpers.cpp", + "CursorWindow.cpp", ], shared_libs: [ "libbase", @@ -147,6 +147,11 @@ cc_library { "libz", ], }, + host_linux: { + srcs: [ + "CursorWindow.cpp", + ], + }, windows: { enabled: true, }, diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp index abf2b0a91642..5e645cceea2d 100644 --- a/libs/androidfw/CursorWindow.cpp +++ b/libs/androidfw/CursorWindow.cpp @@ -18,12 +18,11 @@ #include <androidfw/CursorWindow.h> +#include <sys/mman.h> + #include "android-base/logging.h" -#include "android-base/mapped_file.h" #include "cutils/ashmem.h" -using android::base::MappedFile; - namespace android { /** @@ -40,7 +39,7 @@ CursorWindow::CursorWindow() { CursorWindow::~CursorWindow() { if (mAshmemFd != -1) { - mMappedFile.reset(); + ::munmap(mData, mSize); ::close(mAshmemFd); } else { free(mData); @@ -76,7 +75,6 @@ fail_silent: status_t CursorWindow::maybeInflate() { int ashmemFd = 0; void* newData = nullptr; - std::unique_ptr<MappedFile> mappedFile; // Bail early when we can't expand any further if (mReadOnly || mSize == mInflatedSize) { @@ -97,12 +95,11 @@ status_t CursorWindow::maybeInflate() { goto fail_silent; } - mappedFile = MappedFile::FromFd(ashmemFd, 0, mInflatedSize, PROT_READ | PROT_WRITE); - if (mappedFile == nullptr) { + newData = ::mmap(nullptr, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0); + if (newData == MAP_FAILED) { PLOG(ERROR) << "Failed mmap"; goto fail_silent; } - newData = mappedFile->data(); if (ashmem_set_prot_region(ashmemFd, PROT_READ) < 0) { PLOG(ERROR) << "Failed ashmem_set_prot_region"; @@ -123,7 +120,6 @@ status_t CursorWindow::maybeInflate() { mData = newData; mSize = mInflatedSize; mSlotsOffset = newSlotsOffset; - mMappedFile = std::move(mappedFile); updateSlotsData(); } @@ -134,12 +130,11 @@ status_t CursorWindow::maybeInflate() { fail: LOG(ERROR) << "Failed maybeInflate"; fail_silent: - mappedFile.reset(); + ::munmap(newData, mInflatedSize); ::close(ashmemFd); return UNKNOWN_ERROR; } -#ifdef __linux__ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outWindow) { *outWindow = nullptr; @@ -172,12 +167,11 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outWindow goto fail_silent; } - window->mMappedFile = MappedFile::FromFd(window->mAshmemFd, 0, window->mSize, PROT_READ); - if (window->mMappedFile == nullptr) { + window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, window->mAshmemFd, 0); + if (window->mData == MAP_FAILED) { PLOG(ERROR) << "Failed mmap"; goto fail_silent; } - window->mData = window->mMappedFile->data(); } else { window->mAshmemFd = -1; @@ -241,7 +235,6 @@ fail: fail_silent: return UNKNOWN_ERROR; } -#endif status_t CursorWindow::clear() { if (mReadOnly) { diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h index 0996355cd2c4..9ec026a19c4c 100644 --- a/libs/androidfw/include/androidfw/CursorWindow.h +++ b/libs/androidfw/include/androidfw/CursorWindow.h @@ -23,13 +23,9 @@ #include <string> #include "android-base/stringprintf.h" -#ifdef __linux__ #include "binder/Parcel.h" -#endif #include "utils/String8.h" -#include "android-base/mapped_file.h" - #define LOG_WINDOW(...) namespace android { @@ -84,11 +80,9 @@ public: ~CursorWindow(); static status_t create(const String8& name, size_t size, CursorWindow** outCursorWindow); -#ifdef __linux__ static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow); status_t writeToParcel(Parcel* parcel); -#endif inline String8 name() { return mName; } inline size_t size() { return mSize; } @@ -155,8 +149,6 @@ private: String8 mName; int mAshmemFd = -1; void* mData = nullptr; - std::unique_ptr<android::base::MappedFile> mMappedFile; - /** * Pointer to the first FieldSlot, used to optimize the extremely * hot code path of getFieldSlot(). diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt index e9845c1d9f13..27817e9eb984 100644 --- a/libs/appfunctions/api/current.txt +++ b/libs/appfunctions/api/current.txt @@ -4,7 +4,6 @@ package com.google.android.appfunctions.sidecar { public final class AppFunctionManager { ctor public AppFunctionManager(android.content.Context); method public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>); - method @Deprecated public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>); method public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>); method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>); field public static final int APP_FUNCTION_STATE_DEFAULT = 0; // 0x0 @@ -15,9 +14,7 @@ package com.google.android.appfunctions.sidecar { public abstract class AppFunctionService extends android.app.Service { ctor public AppFunctionService(); method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); - method @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>); - method @Deprecated @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>); - method @Deprecated @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>); + method @MainThread public abstract void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>); field @NonNull public static final String BIND_APP_FUNCTION_SERVICE = "android.permission.BIND_APP_FUNCTION_SERVICE"; field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService"; } diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java index d660926575d1..43377d8eb91c 100644 --- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java +++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java @@ -126,33 +126,6 @@ public final class AppFunctionManager { } /** - * Executes the app function. - * - * <p>Proxies request and response to the underlying {@link - * android.app.appfunctions.AppFunctionManager#executeAppFunction}, converting the request and - * response in the appropriate type required by the function. - * - * @deprecated Use {@link #executeAppFunction(ExecuteAppFunctionRequest, Executor, - * CancellationSignal, Consumer)} instead. This method will be removed once usage references - * are updated. - */ - @Deprecated - public void executeAppFunction( - @NonNull ExecuteAppFunctionRequest sidecarRequest, - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer<ExecuteAppFunctionResponse> callback) { - Objects.requireNonNull(sidecarRequest); - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - - executeAppFunction( - sidecarRequest, - executor, - new CancellationSignal(), - callback); - } - - /** * Returns a boolean through a callback, indicating whether the app function is enabled. * * <p>* This method can only check app functions owned by the caller, or those where the caller diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java index 2a168e871713..0dc87e45b7e3 100644 --- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java +++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java @@ -119,76 +119,9 @@ public abstract class AppFunctionService extends Service { * @param callback A callback to report back the result. */ @MainThread - public void onExecuteFunction( + public abstract void onExecuteFunction( @NonNull ExecuteAppFunctionRequest request, @NonNull String callingPackage, @NonNull CancellationSignal cancellationSignal, - @NonNull Consumer<ExecuteAppFunctionResponse> callback) { - onExecuteFunction(request, cancellationSignal, callback); - } - - /** - * Called by the system to execute a specific app function. - * - * <p>This method is triggered when the system requests your AppFunctionService to handle a - * particular function you have registered and made available. - * - * <p>To ensure proper routing of function requests, assign a unique identifier to each - * function. This identifier doesn't need to be globally unique, but it must be unique within - * your app. For example, a function to order food could be identified as "orderFood". In most - * cases this identifier should come from the ID automatically generated by the AppFunctions - * SDK. You can determine the specific function to invoke by calling {@link - * ExecuteAppFunctionRequest#getFunctionIdentifier()}. - * - * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker - * thread and dispatch the result with the given callback. You should always report back the - * result using the callback, no matter if the execution was successful or not. - * - * @param request The function execution request. - * @param cancellationSignal A {@link CancellationSignal} to cancel the request. - * @param callback A callback to report back the result. - * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, String, - * CancellationSignal, Consumer)} instead. This method will be removed once usage references - * are updated. - */ - @MainThread - @Deprecated - public void onExecuteFunction( - @NonNull ExecuteAppFunctionRequest request, - @NonNull CancellationSignal cancellationSignal, - @NonNull Consumer<ExecuteAppFunctionResponse> callback) { - onExecuteFunction(request, callback); - } - - /** - * Called by the system to execute a specific app function. - * - * <p>This method is triggered when the system requests your AppFunctionService to handle a - * particular function you have registered and made available. - * - * <p>To ensure proper routing of function requests, assign a unique identifier to each - * function. This identifier doesn't need to be globally unique, but it must be unique within - * your app. For example, a function to order food could be identified as "orderFood". In most - * cases this identifier should come from the ID automatically generated by the AppFunctions - * SDK. You can determine the specific function to invoke by calling {@link - * ExecuteAppFunctionRequest#getFunctionIdentifier()}. - * - * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker - * thread and dispatch the result with the given callback. You should always report back the - * result using the callback, no matter if the execution was successful or not. - * - * @param request The function execution request. - * @param callback A callback to report back the result. - * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, CancellationSignal, - * Consumer)} instead. This method will be removed once usage references are updated. - */ - @MainThread - @Deprecated - public void onExecuteFunction( - @NonNull ExecuteAppFunctionRequest request, - @NonNull Consumer<ExecuteAppFunctionResponse> callback) { - Log.w( - "AppFunctionService", - "Calling deprecated default implementation of onExecuteFunction"); - } + @NonNull Consumer<ExecuteAppFunctionResponse> callback); } diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index 28d85bd860df..a3737131702d 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -242,45 +242,59 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { } } -SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const { - const DirtyStack* frame = mHead; - Matrix4 transform; - SkRect pretransformResult = bounds; - while (true) { - SkRect currentBounds = pretransformResult; - pretransformResult.setEmpty(); - switch (frame->type) { - case TransformRenderNode: { - const RenderProperties& props = frame->renderNode->properties(); - // Perform clipping - if (props.getClipDamageToBounds() && !currentBounds.isEmpty()) { - if (!currentBounds.intersect( - SkRect::MakeIWH(props.getWidth(), props.getHeight()))) { - currentBounds.setEmpty(); - } +static void computeClipAndTransformImpl(const DirtyStack* currentFrame, SkRect* crop, + Matrix4* outMatrix) { + SkRect currentCrop = *crop; + switch (currentFrame->type) { + case TransformRenderNode: { + const RenderProperties& props = currentFrame->renderNode->properties(); + // Perform clipping + if (props.getClipDamageToBounds() && !currentCrop.isEmpty()) { + if (!currentCrop.intersect(SkRect::MakeIWH(props.getWidth(), props.getHeight()))) { + currentCrop.setEmpty(); } + } - // apply all transforms - mapRect(props, currentBounds, &pretransformResult); - frame->renderNode->applyViewPropertyTransforms(transform); - } break; - case TransformMatrix4: - mapRect(frame->matrix4, currentBounds, &pretransformResult); - transform.multiply(*frame->matrix4); - break; - default: - pretransformResult = currentBounds; - break; - } - if (frame->prev == frame) break; - frame = frame->prev; + // apply all transforms + crop->setEmpty(); + mapRect(props, currentCrop, crop); + } break; + case TransformMatrix4: + crop->setEmpty(); + mapRect(currentFrame->matrix4, currentCrop, crop); + break; + default: + break; + } + + if (currentFrame->prev != currentFrame) { + computeClipAndTransformImpl(currentFrame->prev, crop, outMatrix); + } + switch (currentFrame->type) { + case TransformRenderNode: + currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix); + break; + case TransformMatrix4: + outMatrix->multiply(*currentFrame->matrix4); + break; + case TransformNone: + // nothing to be done + break; + default: + LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d", + currentFrame->type); } - SkRect result; +} + +SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const { + SkRect cropInGlobal = bounds; + outMatrix->loadIdentity(); + computeClipAndTransformImpl(mHead, &cropInGlobal, outMatrix); + SkRect cropInLocal; Matrix4 globalToLocal; - globalToLocal.loadInverse(transform); - mapRect(&globalToLocal, pretransformResult, &result); - *outMatrix = transform; - return result; + globalToLocal.loadInverse(*outMatrix); + mapRect(&globalToLocal, cropInGlobal, &cropInLocal); + return cropInLocal; } void DamageAccumulator::dirty(float left, float top, float right, float bottom) { diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 84bd45dfc012..b73380e38fd1 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -582,20 +582,22 @@ size_t Bitmap::mTotalBitmapBytes = 0; size_t Bitmap::mTotalBitmapCount = 0; void Bitmap::traceBitmapCreate() { + size_t bytes = getAllocationByteCount(); + std::lock_guard lock{mLock}; + mTotalBitmapBytes += bytes; + mTotalBitmapCount++; if (ATRACE_ENABLED()) { - std::lock_guard lock{mLock}; - mTotalBitmapBytes += getAllocationByteCount(); - mTotalBitmapCount++; ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes); ATRACE_INT64("Bitmap Count", mTotalBitmapCount); } } void Bitmap::traceBitmapDelete() { + size_t bytes = getAllocationByteCount(); + std::lock_guard lock{mLock}; + mTotalBitmapBytes -= getAllocationByteCount(); + mTotalBitmapCount--; if (ATRACE_ENABLED()) { - std::lock_guard lock{mLock}; - mTotalBitmapBytes -= getAllocationByteCount(); - mTotalBitmapCount--; ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes); ATRACE_INT64("Bitmap Count", mTotalBitmapCount); } diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp index 456338631ae4..70e6beda6cb9 100644 --- a/libs/hwui/jni/text/TextShaper.cpp +++ b/libs/hwui/jni/text/TextShaper.cpp @@ -31,12 +31,34 @@ namespace android { +struct FakedFontKey { + uint32_t operator()(const minikin::FakedFont& fakedFont) const { + return minikin::Hasher() + .update(reinterpret_cast<uintptr_t>(fakedFont.font.get())) + .update(fakedFont.fakery.bits()) + .update(fakedFont.fakery.variationSettings()) + .hash(); + } +}; + struct LayoutWrapper { LayoutWrapper(minikin::Layout&& layout, float ascent, float descent) : layout(std::move(layout)), ascent(ascent), descent(descent) {} + + LayoutWrapper(minikin::Layout&& layout, float ascent, float descent, std::vector<jlong>&& fonts, + std::vector<uint32_t>&& fontIds) + : layout(std::move(layout)) + , ascent(ascent) + , descent(descent) + , fonts(std::move(fonts)) + , fontIds(std::move(fontIds)) {} + minikin::Layout layout; float ascent; float descent; + + std::vector<jlong> fonts; + std::vector<uint32_t> fontIds; // per glyph }; static void releaseLayout(jlong ptr) { @@ -64,6 +86,43 @@ static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int cou overallDescent = std::max(overallDescent, extent.descent); } + if (text_feature::typeface_redesign()) { + uint32_t runCount = layout.getFontRunCount(); + + std::unordered_map<minikin::FakedFont, uint32_t, FakedFontKey> fakedToFontIds; + std::vector<jlong> fonts; + std::vector<uint32_t> fontIds; + + fontIds.resize(layout.nGlyphs()); + for (uint32_t ri = 0; ri < runCount; ++ri) { + const minikin::FakedFont& fakedFont = layout.getFontRunFont(ri); + + auto it = fakedToFontIds.find(fakedFont); + uint32_t fontId; + if (it != fakedToFontIds.end()) { + fontId = it->second; // We've seen it. + } else { + fontId = fonts.size(); // This is new to us. Create new one. + std::shared_ptr<minikin::Font> font = std::make_shared<minikin::Font>( + fakedFont.font, fakedFont.fakery.variationSettings()); + fonts.push_back(reinterpret_cast<jlong>(new FontWrapper(std::move(font)))); + fakedToFontIds.insert(std::make_pair(fakedFont, fontId)); + } + + const uint32_t runStart = layout.getFontRunStart(ri); + const uint32_t runEnd = layout.getFontRunEnd(ri); + for (uint32_t i = runStart; i < runEnd; ++i) { + fontIds[i] = fontId; + } + } + + std::unique_ptr<LayoutWrapper> ptr = + std::make_unique<LayoutWrapper>(std::move(layout), overallAscent, overallDescent, + std::move(fonts), std::move(fontIds)); + + return reinterpret_cast<jlong>(ptr.release()); + } + std::unique_ptr<LayoutWrapper> ptr = std::make_unique<LayoutWrapper>( std::move(layout), overallAscent, overallDescent ); @@ -156,6 +215,8 @@ static jboolean TextShaper_Result_getFakeItalic(CRITICAL_JNI_PARAMS_COMMA jlong return layout->layout.getFakery(i).isFakeItalic(); } +constexpr float NO_OVERRIDE = -1; + float findValueFromVariationSettings(const minikin::FontFakery& fakery, minikin::AxisTag tag) { for (const minikin::FontVariation& fv : fakery.variationSettings()) { if (fv.axisTag == tag) { @@ -171,12 +232,7 @@ static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlon if (text_feature::typeface_redesign()) { float value = findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_wght); - if (!std::isnan(value)) { - return value; - } else { - const std::shared_ptr<minikin::Font>& font = layout->layout.getFontRef(i); - return font->style().weight(); - } + return std::isnan(value) ? NO_OVERRIDE : value; } else { return layout->layout.getFakery(i).wghtAdjustment(); } @@ -188,12 +244,7 @@ static jfloat TextShaper_Result_getItalicOverride(CRITICAL_JNI_PARAMS_COMMA jlon if (text_feature::typeface_redesign()) { float value = findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_ital); - if (!std::isnan(value)) { - return value; - } else { - const std::shared_ptr<minikin::Font>& font = layout->layout.getFontRef(i); - return font->style().isItalic(); - } + return std::isnan(value) ? NO_OVERRIDE : value; } else { return layout->layout.getFakery(i).italAdjustment(); } @@ -207,6 +258,24 @@ static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint } // CriticalNative +static jint TextShaper_Result_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr) { + const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); + return layout->fonts.size(); +} + +// CriticalNative +static jlong TextShaper_Result_getFontRef(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint fontId) { + const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); + return layout->fonts[fontId]; +} + +// CriticalNative +static jint TextShaper_Result_getFontId(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint glyphIdx) { + const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); + return layout->fontIds[glyphIdx]; +} + +// CriticalNative static jlong TextShaper_Result_nReleaseFunc(CRITICAL_JNI_PARAMS) { return reinterpret_cast<jlong>(releaseLayout); } @@ -250,6 +319,10 @@ static const JNINativeMethod gResultMethods[] = { {"nGetWeightOverride", "(JI)F", (void*)TextShaper_Result_getWeightOverride}, {"nGetItalicOverride", "(JI)F", (void*)TextShaper_Result_getItalicOverride}, {"nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc}, + + {"nGetFontCount", "(J)I", (void*)TextShaper_Result_getFontCount}, + {"nGetFontRef", "(JI)J", (void*)TextShaper_Result_getFontRef}, + {"nGetFontId", "(JI)I", (void*)TextShaper_Result_getFontId}, }; int register_android_graphics_text_TextShaper(JNIEnv* env) { diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index eeb4853afadc..961962f6a010 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -85,8 +85,7 @@ interface IMediaRouterService { void updateScanningState(IMediaRouter2Manager manager, @JavaPassthrough(annotation="@android.media.MediaRouter2.ScanningState") int scanningState); void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId, - in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route, - in UserHandle transferInitiatorUserHandle, in String transferInitiatorPackageName); + in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route); void selectRouteWithManager(IMediaRouter2Manager manager, int requestId, String sessionId, in MediaRoute2Info route); void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId, diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index b84990b54bd5..3499c438086d 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -2770,7 +2770,7 @@ public final class MediaRouter2 { || isSystemRouteReselection) { transferToRoute(sessionInfo, route, mClientUser, mClientPackageName); } else { - requestCreateSession(sessionInfo, route, mClientUser, mClientPackageName); + requestCreateSession(sessionInfo, route); } } @@ -2826,10 +2826,7 @@ public final class MediaRouter2 { * @param route The {@link MediaRoute2Info route} to transfer to. */ private void requestCreateSession( - @NonNull RoutingSessionInfo oldSession, - @NonNull MediaRoute2Info route, - @NonNull UserHandle transferInitiatorUserHandle, - @NonNull String transferInitiatorPackageName) { + @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) { if (TextUtils.isEmpty(oldSession.getClientPackageName())) { Log.w(TAG, "requestCreateSession: Can't create a session without package name."); this.onTransferFailed(oldSession, route); @@ -2840,12 +2837,7 @@ public final class MediaRouter2 { try { mMediaRouterService.requestCreateSessionWithManager( - mClient, - requestId, - oldSession, - route, - transferInitiatorUserHandle, - transferInitiatorPackageName); + mClient, requestId, oldSession, route); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 8fa0e49e8b96..7e1dccf2d366 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -524,8 +524,7 @@ public final class MediaRouter2Manager { transferToRoute( sessionInfo, route, transferInitiatorUserHandle, transferInitiatorPackageName); } else { - requestCreateSession(sessionInfo, route, transferInitiatorUserHandle, - transferInitiatorPackageName); + requestCreateSession(sessionInfo, route); } } @@ -914,9 +913,7 @@ public final class MediaRouter2Manager { } } - private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route, - @NonNull UserHandle transferInitiatorUserHandle, - @NonNull String transferInitiationPackageName) { + private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route) { if (TextUtils.isEmpty(oldSession.getClientPackageName())) { Log.w(TAG, "requestCreateSession: Can't create a session without package name."); notifyTransferFailed(oldSession, route); @@ -927,8 +924,7 @@ public final class MediaRouter2Manager { try { mMediaRouterService.requestCreateSessionWithManager( - mClient, requestId, oldSession, route, transferInitiatorUserHandle, - transferInitiationPackageName); + mClient, requestId, oldSession, route); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java index 951702ceef0b..d9fd42fd4f7a 100644 --- a/nfc/java/android/nfc/NfcAdapter.java +++ b/nfc/java/android/nfc/NfcAdapter.java @@ -1150,8 +1150,9 @@ public final class NfcAdapter { } /** - * Pauses polling for a {@code timeoutInMs} millis. If polling must be resumed before timeout, - * use {@link #resumePolling()}. + * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. + * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely + * use {@link #resumePolling() to resume the polling. * @hide */ public void pausePolling(int timeoutInMs) { @@ -1210,9 +1211,8 @@ public final class NfcAdapter { } /** - * Resumes default polling for the current device state if polling is paused. Calling - * this while polling is not paused is a no-op. - * + * Resumes default NFC tag reader mode polling for the current device state if polling is + * paused. Calling this while already in polling is a no-op. * @hide */ public void resumePolling() { diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index bc410c7b8ba5..905d6f68a8a0 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -569,8 +569,9 @@ public final class NfcOemExtension { } /** - * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. If polling must be - * resumed before timeout, use {@link #resumePolling()}. + * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. + * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely + * use {@link #resumePolling() to resume the polling. * @param timeoutInMs the pause polling duration in millisecond */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @@ -581,7 +582,7 @@ public final class NfcOemExtension { /** * Resumes default NFC tag reader mode polling for the current device state if polling is - * paused. Calling this while polling is not paused is a no-op. + * paused. Calling this while already in polling is a no-op. */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java index 2983875561c4..d75318f53fe3 100644 --- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -324,7 +324,7 @@ public final class ApduServiceInfo implements Parcelable { mOffHostName = sa.getString( com.android.internal.R.styleable.OffHostApduService_secureElementName); mShouldDefaultToObserveMode = sa.getBoolean( - R.styleable.HostApduService_shouldDefaultToObserveMode, + R.styleable.OffHostApduService_shouldDefaultToObserveMode, false); if (mOffHostName != null) { if (mOffHostName.equals("eSE")) { diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml index d2a0353c9c9b..032eedbf0614 100644 --- a/packages/CompanionDeviceManager/res/values-bn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"আপনি কি <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ম্যানেজ করার জন্য <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>-কে অনুমতি দেবেন?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"ডিভাইস"</string> <string name="summary_glasses" msgid="5469208629679579157">"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-এ এইসব অনুমতি অ্যাক্সেস করার জন্য এই অ্যাপকে অনুমতি দেওয়া হবে"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"আপনার <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এর অ্যাপ ও সিস্টেমের ফিচার <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>-এ স্ট্রিম করার জন্য <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-কে অনুমতি দেবেন?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"অডিও, ফটো, পেমেন্টের তথ্য, পাসওয়ার্ড ও মেসেজ সহ আপনার <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এ দেখা ও চালানো যায় এমন সব কিছু <xliff:g id="APP_NAME_0">%1$s</xliff:g> অ্যাক্সেস করতে পারবে।<br/><br/>আপনি এই অনুমতি না সরানো পর্যন্ত <xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%3$s</xliff:g>-এ অ্যাপের ফিচার স্ট্রিম করতে পারবে।"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"আপনার <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> থেকে অ্যাপ ও সিস্টেমের ফিচার স্ট্রিম করার জন্য <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-এর হয়ে <xliff:g id="APP_NAME">%1$s</xliff:g> অনুমতি চাইছে"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"আপনার <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> থেকে এই তথ্য অ্যাক্সেস করার জন্য <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-কে অনুমতি দিন"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"আপনার <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>-এর ফটো, মিডিয়া ও বিজ্ঞপ্তি অ্যাক্সেস করার জন্য, আপনার <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-এর হয়ে <xliff:g id="APP_NAME">%1$s</xliff:g> অনুমতি চাইছে"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"আপনার <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এর অ্যাপ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?-এ স্ট্রিম করার জন্য <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-কে অনুমতি দেবেন?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"অডিও, ফটো, পাসওয়ার্ড ও মেসেজ সহ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>-এ দেখা ও চালানো যায় এমন সব কিছু <xliff:g id="APP_NAME_0">%1$s</xliff:g> অ্যাক্সেস করতে পারবে।<br/><br/>আপনি এই অনুমতি না সরানো পর্যন্ত <xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>-এ অ্যাপ স্ট্রিম করতে পারবে।"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"আপনার <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> থেকে অ্যাপ স্ট্রিম করার জন্য <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-এর হয়ে <xliff:g id="APP_NAME">%1$s</xliff:g> অনুমতি চাইছে"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string> <string name="summary_generic" msgid="1761976003668044801">"এই অ্যাপ, আপনার ফোন এবং বেছে নেওয়া ডিভাইসের মধ্যে তথ্য সিঙ্ক করতে পারবে, যেমন কোনও কলারের নাম"</string> <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string> diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml index 9b321a8e305d..0dc7001fde70 100644 --- a/packages/CompanionDeviceManager/res/values-ca/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml @@ -25,7 +25,7 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permet que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestioni <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"dispositiu"</string> <string name="summary_glasses" msgid="5469208629679579157">"Aquesta aplicació podrà accedir a aquests permisos del <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <string name="title_app_streaming" msgid="1047090167914857893">"Vols permetre que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> reprodueixi en continu les aplicacions i les funcions del sistema del dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) a <xliff:g id="DEVICE_NAME">%3$s</xliff:g>?"</string> + <string name="title_app_streaming" msgid="1047090167914857893">"Vols permetre que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> reprodueixi en continu les aplicacions i les funcions del sistema del dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) a <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> podrà accedir a qualsevol cosa que sigui visible o que es reprodueixi al teu dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>), inclosos àudios, fotos, informació de pagament, contrasenyes i missatges.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> podrà reproduir en continu aplicacions al dispositiu <xliff:g id="DEVICE_NAME">%3$s</xliff:g> fins que suprimeixis l\'accés a aquest permís."</string> <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del dispositiu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per reproduir en continu aplicacions i funcions del sistema del teu dispositiu (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> @@ -33,7 +33,7 @@ <string name="title_computer" msgid="4782923323932440751">"Permet que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> accedeixi a aquesta informació del <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per accedir a les fotos, el contingut multimèdia i les notificacions del <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vols permetre que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> reprodueixi en continu les aplicacions del dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) a <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g><strong>?"</string> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vols permetre que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> reprodueixi en continu les aplicacions del dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) a <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> podrà accedir a qualsevol cosa que sigui visible o que es reprodueixi al dispositiu <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, inclosos àudios, fotos, informació de pagament, contrasenyes i missatges.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> podrà reproduir en continu aplicacions al dispositiu <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> fins que suprimeixis l\'accés a aquest permís."</string> <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del dispositiu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per reproduir en continu aplicacions del teu dispositiu (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string> diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml index 725a42d92b59..c39145e5f7fc 100644 --- a/packages/CompanionDeviceManager/res/values-de/strings.xml +++ b/packages/CompanionDeviceManager/res/values-de/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Zulassen, dass <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> das Gerät <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> verwalten darf?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"Gerät"</string> <string name="summary_glasses" msgid="5469208629679579157">"Diese App darf dann auf diese Berechtigungen auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>) zugreifen:"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> erlauben, die Apps und Systemfunktionen auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) auf <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> zu streamen?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> hat dann Zugriff auf alle Inhalte, die auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) sichtbar sind oder abgespielt werden, einschließlich Audioinhalten, Fotos, Zahlungsinformationen, Passwörtern und Nachrichten.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> kann so lange Apps auf „<xliff:g id="DEVICE_NAME">%3$s</xliff:g>“ streamen, bis du diese Berechtigung entfernst."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein Gerät <xliff:g id="DEVICE_NAME">%2$s</xliff:g> um die Berechtigung, Apps und Systemfunktionen von deinem Gerät (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) zu streamen"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> Zugriff auf diese Informationen von deinem Gerät (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) gewähren"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet im Namen von „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“ um die Berechtigung, auf die Fotos, Medien und Benachrichtigungen auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) zuzugreifen"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> erlauben, die Apps auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) auf <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> zu streamen?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> hat dann Zugriff auf alle Inhalte, die auf deinem Gerät (<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>) sichtbar sind oder abgespielt werden, einschließlich Audioinhalten, Fotos, Zahlungsinformationen, Passwörtern und Nachrichten.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> kann so lange Apps auf „<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>“ streamen, bis du diese Berechtigung entfernst."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein Gerät <xliff:g id="DEVICE_NAME">%2$s</xliff:g> um die Berechtigung, Apps von deinem Gerät (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) zu streamen"</string> <string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string> <string name="summary_generic" msgid="1761976003668044801">"Diese App kann dann Daten wie den Namen eines Anrufers zwischen deinem Smartphone und dem ausgewählten Gerät synchronisieren"</string> <string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string> diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml index 9f6219274258..a7a408628690 100644 --- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permite que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> administre <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> <string name="summary_glasses" msgid="5469208629679579157">"Esta app podrá acceder a los siguientes permisos en tu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"¿Quieres permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> transmita las apps y las funciones del sistema de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> a <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> tendrá acceso a todo el contenido visible o que se reproduzca en tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, lo que incluye audio, fotos, información de pago, contraseñas y mensajes.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> podrá transmitir apps a <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hasta que se quite el acceso a este permiso."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita permiso en nombre de <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para transmitir apps y funciones del sistema desde tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Permite que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para acceder a las fotos, el contenido multimedia y las notificaciones de tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"¿Quieres permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> transmita las apps de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> a <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> tendrá acceso a todo el contenido visible o que se reproduzca en <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, lo que incluye audio, fotos, información de pago, contraseñas y mensajes.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> podrá transmitir apps a <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> hasta que se quite el acceso a este permiso."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita permiso en nombre de <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para transmitir apps desde tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="1761976003668044801">"Esta app podrá sincronizar información, como el nombre de la persona que llama, entre el teléfono y el dispositivo elegido"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml index 44474b96b44c..8816e6d48eaa 100644 --- a/packages/CompanionDeviceManager/res/values-es/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"¿Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestione <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> <string name="summary_glasses" msgid="5469208629679579157">"Esta aplicación podrá acceder a estos permisos de tu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"¿Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> emita las aplicaciones y funciones del sistema de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> en <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> tendrá acceso a todo lo que se vea o se reproduzca en tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, incluidos audio, fotos, información para pagos, contraseñas y mensajes.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> podrá emitir aplicaciones en <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hasta que quites el acceso a este permiso."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para emitir aplicaciones y funciones del sistema desde tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para acceder a las fotos, los archivos multimedia y las notificaciones de tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"¿Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> emita las aplicaciones de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> en <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> tendrá acceso a todo lo que se vea o se reproduzca en <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, incluidos audio, fotos, información para pagos, contraseñas y mensajes.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> podrá emitir aplicaciones en <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> hasta que quites el acceso a este permiso."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para emitir aplicaciones desde tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="1761976003668044801">"Esta aplicación podrá sincronizar información (por ejemplo, el nombre de la persona que te llama) entre tu teléfono y el dispositivo que elijas"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml index b5de650d356b..c4a8447d2aed 100644 --- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à gérer <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"appareil"</string> <string name="summary_glasses" msgid="5469208629679579157">"Cette appli pourra accéder à ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à diffuser les applis et les fonctionnalités du système de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> vers <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aura accès à tout ce qui est visible ou lu sur votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, y compris le contenu audio, les photos, les infos de paiement, les mots de passe et les messages.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> pourra diffuser des applis vers <xliff:g id="DEVICE_NAME">%3$s</xliff:g> jusqu\'à ce que vous retiriez l\'accès à cette autorisation."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de diffuser des applis et des fonctionnalités système à partir de votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à ces informations à partir de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g> pour accéder aux photos, aux fichiers multimédias et aux notifications de votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à diffuser les applis de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> vers <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aura accès à tout ce qui est visible ou lu sur votre <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, y compris le contenu audio, les photos, les infos de paiement, les mots de passe et les messages.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> pourra diffuser des applis vers <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> jusqu\'à ce que vous retiriez l\'accès à cette autorisation."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de diffuser des applis à partir de votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string> <string name="summary_generic" msgid="1761976003668044801">"Cette appli pourra synchroniser des informations, comme le nom de l\'appelant, entre votre téléphone et l\'appareil sélectionné"</string> <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string> diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml index b4933ee2279f..88627e580e1e 100644 --- a/packages/CompanionDeviceManager/res/values-fr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à gérer <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"appareil"</string> <string name="summary_glasses" msgid="5469208629679579157">"Cette appli sera autorisée à accéder à ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à caster les applis et les fonctionnalités système de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> sur <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aura accès à tout ce qui est visible ou lu sur votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, y compris les contenus audio, les photos, les infos de paiement, les mots de passe et les messages.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> pourra caster des applis sur <xliff:g id="DEVICE_NAME">%3$s</xliff:g> jusqu\'à ce que vous supprimiez l\'accès à cette autorisation."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande, au nom de l\'appareil <xliff:g id="DEVICE_NAME">%2$s</xliff:g>, l\'autorisation de caster des applis et des fonctionnalités système depuis votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à ces informations depuis votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g> pour accéder aux photos, multimédias et notifications de votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à caster les applis de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> sur <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aura accès à tout ce qui est visible ou lu sur votre <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, y compris les contenus audio, les photos, les infos de paiement, les mots de passe et les messages.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> pourra caster des applis sur <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> jusqu\'à ce que vous supprimiez l\'accès à cette autorisation."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande, au nom de l\'appareil <xliff:g id="DEVICE_NAME">%2$s</xliff:g>, l\'autorisation de caster des applis depuis votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string> <string name="summary_generic" msgid="1761976003668044801">"Cette appli pourra synchroniser des infos, comme le nom de l\'appelant, entre votre téléphone et l\'appareil choisi"</string> <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string> diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml index a4238c0ef46c..744168b4e7d8 100644 --- a/packages/CompanionDeviceManager/res/values-hy/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Թույլատրե՞լ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին կառավարել <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> սարքը"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"սարք"</string> <string name="summary_glasses" msgid="5469208629679579157">"Այս հավելվածը կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ում"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Թույլատրե՞լ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին հեռարձակել ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ի հավելվածները և համակարգի գործառույթները <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> սարքին։"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> հավելվածին հասանելի կլինի ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ում ցուցադրվող կամ նվագարկվող բովանդակությունը՝ ներառյալ աուդիոն, լուսանկարները, վճարային տեղեկությունները, գաղտնաբառերը և հաղորդագրությունները։<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> հավելվածը կկարողանա հավելվածներ հեռարձակել <xliff:g id="DEVICE_NAME">%3$s</xliff:g> սարքին, քանի դեռ չեք չեղարկել այս թույլտվությունը։"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը <xliff:g id="DEVICE_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ից հավելվածներ և համակարգի գործառույթներ հեռարձակելու համար"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Թույլատրեք <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին օգտագործել այս տեղեկությունները ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ից"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DEVICE_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ի լուսանկարները, մեդիաֆայլերն ու ծանուցումները տեսնելու համար"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Թույլատրե՞լ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին հեռարձակել ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ի հավելվածները <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> սարքին։"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> հավելվածին հասանելի կլինի ձեր <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>-ում ցուցադրվող կամ նվագարկվող բովանդակությունը՝ ներառյալ աուդիոն, լուսանկարները, վճարային տեղեկությունները, գաղտնաբառերը և հաղորդագրությունները։<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> հավելվածը կկարողանա հավելվածներ հեռարձակել <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> սարքին, քանի դեռ չեք չեղարկել այս թույլտվությունը։"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը <xliff:g id="DEVICE_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ից հավելվածներ հեռարձակելու համար"</string> <string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string> <string name="summary_generic" msgid="1761976003668044801">"Այս հավելվածը կկարողանա համաժամացնել ձեր հեռախոսի և ընտրված սարքի տվյալները, օր․՝ զանգողի անունը"</string> <string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string> diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml index 2fdcaf0d9852..fe4cc151e2a7 100644 --- a/packages/CompanionDeviceManager/res/values-it/strings.xml +++ b/packages/CompanionDeviceManager/res/values-it/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vuoi consentire all\'app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di gestire <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> <string name="summary_glasses" msgid="5469208629679579157">"Questa app potrà accedere alle seguenti autorizzazioni <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>:"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Consentire all\'app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di riprodurre in streaming le app e le funzionalità di sistema <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> su <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> avrà accesso a tutti i contenuti visibili o riprodotti dal tuo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inclusi audio, foto, dati di pagamento, password e messaggi.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> sarà in grado di riprodurre in streaming le app su <xliff:g id="DEVICE_NAME">%3$s</xliff:g> finché non rimuoverai l\'accesso a questa autorizzazione."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede l\'autorizzazione per conto di <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per riprodurre in streaming app e funzionalità di sistema dal tuo <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Consenti all\'app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di accedere a queste informazioni <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto di <xliff:g id="DEVICE_NAME">%2$s</xliff:g> l\'autorizzazione ad accedere a foto, contenuti multimediali e notifiche <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Consentire all\'app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di riprodurre in streaming le app <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> su <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> avrà accesso a tutti i contenuti visibili o riprodotti dal tuo <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, inclusi audio, foto, dati di pagamento, password e messaggi.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> sarà in grado di riprodurre in streaming le app su <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> finché non rimuoverai l\'accesso a questa autorizzazione."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede l\'autorizzazione per conto di <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per riprodurre in streaming le app dal tuo <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="1761976003668044801">"Questa app potrà sincronizzare informazioni, ad esempio il nome di un chiamante, tra il telefono e il dispositivo scelto"</string> <string name="consent_yes" msgid="8344487259618762872">"Consenti"</string> diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml index c0e48b32b1a5..a69a12e49b34 100644 --- a/packages/CompanionDeviceManager/res/values-mk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Ќе дозволите <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управува со <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"уред"</string> <string name="summary_glasses" msgid="5469208629679579157">"Апликацијава ќе може да пристапува до овие дозволи на <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Да се дозволи <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да ги стримува апликациите и системските функции од <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ќе има пристап до сè што е видливо или репродуцирано на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, вклучувајќи ги и аудиото, фотографиите, податоците за плаќање, лозинките и пораките.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> ќе може да стримува апликации на <xliff:g id="DEVICE_NAME">%3$s</xliff:g> сѐ додека не ја отстраните дозволава."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на <xliff:g id="DEVICE_NAME">%2$s</xliff:g> за да стримува апликации и системски функции од вашиот <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Дозволете <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да пристапува до овие податоци на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DEVICE_NAME">%2$s</xliff:g> за да пристапува до фотографиите, аудиовизуелните содржини и известувањата на <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Да се дозволи <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да ги стримува апликациите од <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ќе има пристап до сè што е видливо или репродуцирано на <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, вклучувајќи ги и аудиото, фотографиите, податоците за плаќање, лозинките и пораките.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> ќе може да стримува апликации на <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> сѐ додека не ја отстраните дозволава."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на <xliff:g id="DEVICE_NAME">%2$s</xliff:g> за да стримува апликации од вашиот <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"уред"</string> <string name="summary_generic" msgid="1761976003668044801">"Оваа апликација ќе може да ги синхронизира податоците како што се имињата на јавувачите помеѓу вашиот телефон и избраниот уред"</string> <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string> diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml index 543bdfa03eed..96951ad9c2d7 100644 --- a/packages/CompanionDeviceManager/res/values-mn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-д <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>-г удирдахыг зөвшөөрөх үү?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"төхөөрөмж"</string> <string name="summary_glasses" msgid="5469208629679579157">"Энэ аппад таны <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> дээрх эдгээр зөвшөөрөлд хандахыг зөвшөөрнө"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-д таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н апп болон системийн онцлогийг <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>-д дамжуулахыг зөвшөөрөх үү?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> аудио, зураг, төлбөрийн мэдээлэл, нууц үг, мессеж зэрэг таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> дээр харагдаж, тоглуулж буй аливаа зүйлд хандах эрхтэй байх болно.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> таныг энэ зөвшөөрөлд хандах эрхийг нь хасах хүртэл <xliff:g id="DEVICE_NAME">%3$s</xliff:g>-д апп дамжуулах боломжтой байх болно."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-н өмнөөс таны <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>-с апп болон системийн онцлог дамжуулах зөвшөөрлийг хүсэж байна"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-д таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н энэ мэдээлэлд хандахыг зөвшөөрнө үү"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-н өмнөөс <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>-н зураг, медиа, мэдэгдэлд хандах зөвшөөрлийг хүсэж байна"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-д таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н аппыг <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>-д дамжуулахыг зөвшөөрөх үү?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> аудио, зураг, төлбөрийн мэдээлэл, нууц үг, мессеж зэрэг <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> дээр харагдаж, тоглуулж буй аливаа зүйлд хандах эрхтэй болно.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> таныг энэ зөвшөөрөлд хандах эрхийг нь хасах хүртэл <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>-д апп дамжуулах боломжтой байх болно."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-н өмнөөс таны <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>-с апп дамжуулах зөвшөөрлийг хүсэж байна"</string> <string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string> <string name="summary_generic" msgid="1761976003668044801">"Энэ апп залгаж буй хүний нэр зэрэг мэдээллийг таны утас болон сонгосон төхөөрөмжийн хооронд синк хийх боломжтой болно"</string> <string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string> diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml index 13916b7c84ab..b3c8bd0a4462 100644 --- a/packages/CompanionDeviceManager/res/values-ms/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml @@ -26,7 +26,7 @@ <string name="profile_name_glasses" msgid="3506504967216601277">"peranti"</string> <string name="summary_glasses" msgid="5469208629679579157">"Apl ini akan dibenarkan untuk mengakses kebenaran yang berikut pada <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> anda"</string> <string name="title_app_streaming" msgid="1047090167914857893">"Benarkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk menstrim apl dan ciri sistem <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda kepada <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> - <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan mendapat akses kepada semua perkara yang dipaparkan atau dimainkan pada <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda, termasuk audio, foto, maklumat pembayaran, kata laluan dan mesej.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> akan dapat menstrim apl kepada <xliff:g id="DEVICE_NAME">%3$s</xliff:g> sehingga anda mengalih keluar akses kepada kebenaran ini."</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan mendapat akses kepada semua kandungan yang dipaparkan atau dimainkan pada <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda, termasuk audio, foto, maklumat pembayaran, kata laluan dan mesej.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> akan dapat menstrim apl kepada <xliff:g id="DEVICE_NAME">%3$s</xliff:g> sehingga anda mengalih keluar akses kepada kebenaran ini."</string> <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta kebenaran bagi pihak <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk menstrim apl dan ciri sistem daripada <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> anda"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> @@ -34,7 +34,7 @@ <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang meminta kebenaran bagi pihak <xliff:g id="DEVICE_NAME">%2$s</xliff:g> anda untuk mengakses foto, media dan pemberitahuan <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> anda"</string> <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Benarkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk menstrim apl <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda kepada <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> - <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan mendapat akses kepada semua perkara yang dipaparkan atau dimainkan pada <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, termasuk audio, foto, maklumat pembayaran, kata laluan dan mesej.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> akan dapat menstrim apl kepada <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> sehingga anda mengalih keluar akses kepada kebenaran ini."</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan mendapat akses kepada semua kandungan yang dipaparkan atau dimainkan pada <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, termasuk audio, foto, maklumat pembayaran, kata laluan dan mesej.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> akan dapat menstrim apl kepada <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> sehingga anda mengalih keluar akses kepada kebenaran ini."</string> <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta kebenaran bagi pihak <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk menstrim apl daripada <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> anda"</string> <string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string> <string name="summary_generic" msgid="1761976003668044801">"Apl ini akan dapat menyegerakkan maklumat seperti nama individu yang memanggil, antara telefon anda dengan peranti yang dipilih"</string> diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml index 14acdf0d3158..33d243017248 100644 --- a/packages/CompanionDeviceManager/res/values-sq/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Të lejohet që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të menaxhojë <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"pajisje"</string> <string name="summary_glasses" msgid="5469208629679579157">"Këtij aplikacioni do t\'i lejohet qasja te këto leje te <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Të lejohet që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të transmetojë aplikacionet dhe veçoritë e sistemit nga <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> te <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> do të ketë qasje te çdo gjë që është e dukshme ose që luhet te <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, duke përfshirë audion, fotografitë, informacionet për pagesën, fjalëkalimet dhe mesazhet.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> do të mund t\'i transmetojë aplikacionet në <xliff:g id="DEVICE_NAME">%3$s</xliff:g> derisa ta heqësh qasjen për këtë leje."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DEVICE_NAME">%2$s</xliff:g> për të transmetuar aplikacione dhe veçori të sistemit nga <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Lejo që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të ketë qasje në këto informacione te <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DEVICE_NAME">%2$s</xliff:g> për të marrë qasje te fotografitë, media dhe njoftimet te <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Të lejohet që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të transmetojë aplikacionet nga <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> te <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> do të ketë qasje te çdo gjë që është e dukshme ose që luhet te <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, duke përfshirë audion, fotografitë, informacionet për pagesën, fjalëkalimet dhe mesazhet.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> do të mund t\'i transmetojë aplikacionet në <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> derisa ta heqësh qasjen për këtë leje."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DEVICE_NAME">%2$s</xliff:g> për të transmetuar aplikacione nga <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string> <string name="summary_generic" msgid="1761976003668044801">"Ky aplikacion do të mund të sinkronizojë informacione, si p.sh emrin e dikujt që po telefonon, mes telefonit tënd dhe pajisjes së zgjedhur."</string> <string name="consent_yes" msgid="8344487259618762872">"Lejo"</string> diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml index 76e6410e8d32..202ec79c64e1 100644 --- a/packages/CompanionDeviceManager/res/values-ta/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong&gt சாதனத்தை நிர்வகிக்க <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதிக்கவா?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"சாதனம்"</string> <string name="summary_glasses" msgid="5469208629679579157">"உங்கள் <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> சாதனத்தில் இந்த அனுமதிகளை அணுக இந்த ஆப்ஸ் அனுமதிக்கப்படும்"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சாதனத்தின் ஆப்ஸையும் சிஸ்டம் அம்சங்களையும் <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> சாதனத்தில் ஸ்ட்ரீம் செய்ய <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதிக்கவா?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"ஆடியோ, படங்கள், பேமெண்ட் தகவல்கள், கடவுச்சொற்கள், மெசேஜ்கள் உட்பட <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சாதனத்தில் காட்டப்படுகின்ற/பிளே செய்யப்படுகின்ற அனைத்தையும் <xliff:g id="APP_NAME_0">%1$s</xliff:g> அணுகும்.<br/><br/>இந்த அனுமதிக்கான அணுகலை நீங்கள் அகற்றும் வரை <xliff:g id="DEVICE_NAME">%3$s</xliff:g> சாதனத்தில் ஆப்ஸை <xliff:g id="APP_NAME_1">%1$s</xliff:g> ஸ்ட்ரீம் செய்ய முடியும்."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"உங்கள் <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> சாதனத்தில் இருந்து ஆப்ஸையும் சிஸ்டம் அம்சங்களையும் ஸ்ட்ரீம் செய்ய உங்கள் <xliff:g id="DEVICE_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கேட்கிறது"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சாதனத்தில் உள்ள இந்தத் தகவல்களை அணுக, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதிக்கவும்"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"உங்கள் <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> சாதனத்தில் உள்ள படங்கள், மீடியா, அறிவிப்புகள் ஆகியவற்றை அணுக உங்கள் <xliff:g id="DEVICE_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கேட்கிறது"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சாதனத்தின் ஆப்ஸை <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> சாதனத்தில் ஸ்ட்ரீம் செய்ய <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதிக்கவா?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"ஆடியோ, படங்கள், பேமெண்ட் தகவல்கள், கடவுச்சொற்கள், மெசேஜ்கள் உட்பட <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> சாதனத்தில் காட்டப்படுகின்ற/பிளே செய்யப்படுகின்ற அனைத்தையும் <xliff:g id="APP_NAME_0">%1$s</xliff:g> அணுகும்.<br/><br/>இந்த அனுமதிக்கான அணுகலை நீங்கள் அகற்றும் வரை <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> சாதனத்தில் ஆப்ஸை <xliff:g id="APP_NAME_2">%1$s</xliff:g> ஸ்ட்ரீம் செய்ய முடியும்."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"உங்கள் <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> சாதனத்தில் இருந்து ஆப்ஸை ஸ்ட்ரீம் செய்ய உங்கள் <xliff:g id="DEVICE_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கேட்கிறது"</string> <string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string> <string name="summary_generic" msgid="1761976003668044801">"அழைப்பவரின் பெயர் போன்ற தகவலை உங்கள் மொபைல் மற்றும் தேர்வுசெய்த சாதனத்திற்கு இடையில் இந்த ஆப்ஸால் ஒத்திசைக்க முடியும்"</string> <string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string> diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml index 44d6bf7da32c..7acad6475875 100644 --- a/packages/CompanionDeviceManager/res/values-tr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasına <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazını yönetmesi için izin verilsin mi?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"Cihaz"</string> <string name="summary_glasses" msgid="5469208629679579157">"Bu uygulamanın <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> cihazınızda şu izinlere erişmesine izin verilecek:"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> adlı uygulamanın <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınızdaki uygulamaları ve sistem özelliklerini <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> cihazına aktarmasına izin verilsin mi?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>; ses, fotoğraflar, ödeme bilgileri, şifreler ve mesajlar da dahil olmak üzere <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınızda görünen veya oynatılan her şeye erişebilecek.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> siz bu iznin erişimini kaldırana kadar uygulamaları <xliff:g id="DEVICE_NAME">%3$s</xliff:g> cihazına aktarabilecek."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adına uygulamaları ve sistem özelliklerini <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> cihazınızdan aktarmak için izin istiyor"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasının, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınızdaki bu bilgilere erişmesine izin verin"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> içindeki fotoğraf, medya ve bildirimlere erişmek için <xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazınız adına izin istiyor"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> adlı uygulamanın <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınızdaki uygulamaları <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> cihazına aktarmasına izin verilsin mi?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>; ses, fotoğraflar, ödeme bilgileri, şifreler ve mesajlar da dahil olmak üzere <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> cihazında görünen veya oynatılan her şeye erişebilecek.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> siz bu iznin erişimini kaldırana kadar uygulamaları <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> cihazına aktarabilecek."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adına uygulamaları <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> cihazınızdan aktarmak için izin istiyor"</string> <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string> <string name="summary_generic" msgid="1761976003668044801">"Bu uygulama, arayan kişinin adı gibi bilgileri telefonunuz ve seçili cihaz arasında senkronize edebilir"</string> <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string> diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml index 1d248b6570fc..50f93d53a660 100644 --- a/packages/CompanionDeviceManager/res/values-uk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Дозволити додатку <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> керувати пристроєм <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"пристрій"</string> <string name="summary_glasses" msgid="5469208629679579157">"Цей додаток матиме доступ до перелічених нижче дозволів на вашому <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Дозволити пристрою <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> транслювати додатки й системні функції на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на пристрій <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"Додаток <xliff:g id="APP_NAME_0">%1$s</xliff:g> матиме доступ до контенту, що відображається чи відтворюється на вашому <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, зокрема до аудіо, фото, платіжної інформації, паролів і повідомлень.<br/><br/>Додаток <xliff:g id="APP_NAME_1">%1$s</xliff:g> зможе транслювати додатки на пристрій \"<xliff:g id="DEVICE_NAME">%3$s</xliff:g>\", поки ви не скасуєте цей дозвіл."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені пристрою \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запитує дозвіл на трансляцію додатків і системних функцій на вашому <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Дозвольте додатку <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ до цієї інформації на вашому <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запитує дозвіл на доступ до фотографій, медіафайлів і сповіщень на вашому <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Дозволити додатку <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> транслювати додатки на вашому <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на пристрій <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Додаток <xliff:g id="APP_NAME_0">%1$s</xliff:g> матиме доступ до контенту, що відображається чи відтворюється на пристрої \"<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>\", зокрема до аудіо, фото, платіжної інформації, паролів і повідомлень.<br/><br/>Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> зможе транслювати додатки на пристрій \"<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>\", поки ви не скасуєте цей дозвіл."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені пристрою \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запитує дозвіл на трансляцію додатків на вашому <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string> <string name="summary_generic" msgid="1761976003668044801">"Цей додаток зможе синхронізувати інформацію (наприклад, ім’я абонента, який викликає) між телефоном і вибраним пристроєм"</string> <string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt index 244b604a87f9..41b4e9bf318d 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt @@ -19,7 +19,10 @@ package com.android.credentialmanager.common.ui import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.icons.Icons @@ -61,8 +64,15 @@ fun Snackbar( ) } Box( - modifier = Modifier - .align(Alignment.BottomCenter).wrapContentSize().padding(bottom = 18.dp) + modifier = + Modifier.align(Alignment.BottomCenter) + .wrapContentSize() + .padding( + bottom = + WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 24.dp, + start = 24.dp, + end = 24.dp, + ) ) { Card( shape = Shapes.medium, diff --git a/packages/PrintSpooler/res/values-kn/strings.xml b/packages/PrintSpooler/res/values-kn/strings.xml index 27279a7e8fc9..93de16f9fa0a 100644 --- a/packages/PrintSpooler/res/values-kn/strings.xml +++ b/packages/PrintSpooler/res/values-kn/strings.xml @@ -33,7 +33,7 @@ <string name="pages_range_example" msgid="8558694453556945172">"ಉದಾ. 1—5,8,11—13"</string> <string name="print_preview" msgid="8010217796057763343">"ಮುದ್ರಣ ಪೂರ್ವವೀಕ್ಷಣೆ"</string> <string name="install_for_print_preview" msgid="6366303997385509332">"ಪೂರ್ವವೀಕ್ಷಣೆಗಾಗಿ PDF ವೀಕ್ಷಕವನ್ನು ಸ್ಥಾಪಿಸಿ"</string> - <string name="printing_app_crashed" msgid="854477616686566398">"ಮುದ್ರಣದ ಅಪ್ಲಿಕೇಶನ್ ಕ್ರ್ಯಾಶ್ ಆಗಿದೆ"</string> + <string name="printing_app_crashed" msgid="854477616686566398">"ಮುದ್ರಣದ ಆ್ಯಪ್ ಕ್ರ್ಯಾಶ್ ಆಗಿದೆ"</string> <string name="generating_print_job" msgid="3119608742651698916">"ಮುದ್ರಣ ಕಾರ್ಯ ರಚಿಸಲಾಗುತ್ತಿದೆ"</string> <string name="save_as_pdf" msgid="5718454119847596853">"PDF ರೂಪದಲ್ಲಿ ಸೇವ್ ಮಾಡಿ"</string> <string name="all_printers" msgid="5018829726861876202">"ಎಲ್ಲಾ ಪ್ರಿಂಟರ್ಗಳು…"</string> diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java index b2861826a103..601e001f48c2 100644 --- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java +++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java @@ -57,7 +57,7 @@ import java.util.List; * 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false) * 2. User doesn't set any title or icon for button. */ -public class ActionButtonsPreference extends Preference { +public class ActionButtonsPreference extends Preference implements GroupSectionDividerMixin { private static final String TAG = "ActionButtonPreference"; private static final boolean mIsAtLeastS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S; diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java index 6cd777e878fe..10769ecfbe42 100644 --- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java @@ -43,7 +43,7 @@ import com.android.settingslib.widget.preference.banner.R; * Banner message is a banner displaying important information (permission request, page error etc), * and provide actions for user to address. It requires a user action to be dismissed. */ -public class BannerMessagePreference extends Preference { +public class BannerMessagePreference extends Preference implements GroupSectionDividerMixin { public enum AttentionLevel { HIGH(0, R.color.banner_background_attention_high, R.color.banner_accent_attention_high), diff --git a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt index eb14746a0f22..84ff1bbef65d 100644 --- a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt +++ b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt @@ -30,7 +30,7 @@ class CardPreference @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin { init { layoutResource = R.layout.settingslib_expressive_preference_card diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml index 33519cba2940..dd7eac776583 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml @@ -26,12 +26,12 @@ android:outlineAmbientShadowColor="@android:color/transparent" android:outlineSpotShadowColor="@android:color/transparent" android:background="@android:color/transparent" - android:theme="@style/Theme.CollapsingToolbar.Settings"> + android:theme="@style/ThemeOverlay.MaterialComponents.PlatformBridge.CollapsingToolbar"> <Toolbar android:id="@+id/action_bar" android:layout_width="match_parent" - android:layout_height="?attr/actionBarSize" + android:layout_height="?android:attr/actionBarSize" android:theme="?android:attr/actionBarTheme" android:transitionName="shared_element_view" app:layout_collapseMode="pin"/> diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml index c20beaf9bf93..02f171cf0d9e 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml @@ -21,4 +21,15 @@ <item name="colorPrimary">@color/settingslib_primary_dark_device_default_settings</item> <item name="colorAccent">@color/settingslib_accent_device_default_dark</item> </style> -</resources>
\ No newline at end of file + + <!-- + ~ TODO(b/349675008): Remove this theme overlay once the platform bridge theme properly sets + ~ the MaterialComponents colors based on the platform theme. + --> + <style name="ThemeOverlay.MaterialComponents.PlatformBridge.CollapsingToolbar"> + <item name="elevationOverlayEnabled">true</item> + <item name="elevationOverlayColor">?attr/colorPrimary</item> + <item name="colorPrimary">@color/settingslib_primary_dark_device_default_settings</item> + <item name="colorAccent">@color/settingslib_accent_device_default_dark</item> + </style> +</resources> diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml index 9ecc297c6d36..403931764d7e 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml @@ -21,4 +21,15 @@ <item name="colorPrimary">@color/settingslib_primary_device_default_settings_light</item> <item name="colorAccent">@color/settingslib_accent_device_default_light</item> </style> -</resources>
\ No newline at end of file + + <!-- + ~ TODO(b/349675008): Remove this theme overlay once the platform bridge theme properly sets + ~ the MaterialComponents colors based on the platform theme. + --> + <style name="ThemeOverlay.MaterialComponents.PlatformBridge.CollapsingToolbar"> + <item name="elevationOverlayEnabled">true</item> + <item name="elevationOverlayColor">?attr/colorPrimary</item> + <item name="colorPrimary">@color/settingslib_primary_device_default_settings_light</item> + <item name="colorAccent">@color/settingslib_accent_device_default_light</item> + </style> +</resources> diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes_bridge.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes_bridge.xml new file mode 100644 index 000000000000..bcb9baf94706 --- /dev/null +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes_bridge.xml @@ -0,0 +1,176 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- See appcompat/appcompat/THEMES for the theme structure. --> +<resources> + <!-- + ~ Bridge theme overlay to simulate AppCompat themes based on a platform theme. + ~ Only non-widget attributes are included here since we should still use the platform widgets. + ~ Only public theme attributes (as in platform public-final.xml) can be referenced here since + ~ this is used in modules. + --> + <style name="Base.V31.ThemeOverlay.AppCompat.PlatformBridge" parent=""> + <!-- START Base.V7.Theme.AppCompat --> + + <item name="colorBackgroundFloating">?android:colorBackgroundFloating</item> + + <item name="isLightTheme">?android:isLightTheme</item> + + <item name="selectableItemBackground">?android:selectableItemBackground</item> + <item name="selectableItemBackgroundBorderless">?android:selectableItemBackgroundBorderless</item> + <item name="homeAsUpIndicator">?android:homeAsUpIndicator</item> + + <item name="dividerVertical">?android:dividerVertical</item> + <item name="dividerHorizontal">?android:dividerHorizontal</item> + + <!-- List attributes --> + <item name="textAppearanceListItem">?android:textAppearanceListItem</item> + <item name="textAppearanceListItemSmall">?android:textAppearanceListItemSmall</item> + <item name="textAppearanceListItemSecondary">?android:textAppearanceListItemSecondary</item> + <item name="listPreferredItemHeight">?android:listPreferredItemHeight</item> + <item name="listPreferredItemHeightSmall">?android:listPreferredItemHeightSmall</item> + <item name="listPreferredItemHeightLarge">?android:listPreferredItemHeightLarge</item> + <item name="listPreferredItemPaddingLeft">?android:listPreferredItemPaddingLeft</item> + <item name="listPreferredItemPaddingRight">?android:listPreferredItemPaddingRight</item> + <item name="listPreferredItemPaddingStart">?android:listPreferredItemPaddingStart</item> + <item name="listPreferredItemPaddingEnd">?android:listPreferredItemPaddingEnd</item> + + <!-- Color palette --> + <item name="colorPrimaryDark">?android:colorPrimaryDark</item> + <item name="colorPrimary">?android:colorPrimary</item> + <item name="colorAccent">?android:colorAccent</item> + + <item name="colorControlNormal">?android:colorControlNormal</item> + <item name="colorControlActivated">?android:colorControlActivated</item> + <item name="colorControlHighlight">?android:colorControlHighlight</item> + <item name="colorButtonNormal">?android:colorButtonNormal</item> + + <item name="colorError">?android:colorError</item> + + <!-- END Base.V7.Theme.AppCompat --> + </style> + <style name="Base.ThemeOverlay.AppCompat.PlatformBridge" parent="Base.V31.ThemeOverlay.AppCompat.PlatformBridge" /> + <style name="ThemeOverlay.AppCompat.PlatformBridge" parent="Base.ThemeOverlay.AppCompat.PlatformBridge" /> + + <!-- + ~ Bridge theme overlay to simulate MaterialComponents themes based on a platform theme. + --> + <style name="Base.V31.ThemeOverlay.MaterialComponents.PlatformBridge" parent="ThemeOverlay.AppCompat.PlatformBridge"> + <!-- START Base.V14.Theme.MaterialComponents.Bridge --> + <!-- + ~ This is copied as-is from the original bridge theme since it is guaranteed to not affect + ~ existing widgets. + --> + + <item name="isMaterialTheme">true</item> + + <item name="colorPrimaryVariant">@color/design_dark_default_color_primary_variant</item> + <item name="colorSecondary">@color/design_dark_default_color_secondary</item> + <item name="colorSecondaryVariant">@color/design_dark_default_color_secondary_variant</item> + <item name="colorSurface">@color/design_dark_default_color_surface</item> + <item name="colorPrimarySurface">?attr/colorSurface</item> + <item name="colorOnPrimary">@color/design_dark_default_color_on_primary</item> + <item name="colorOnSecondary">@color/design_dark_default_color_on_secondary</item> + <item name="colorOnBackground">@color/design_dark_default_color_on_background</item> + <item name="colorOnError">@color/design_dark_default_color_on_error</item> + <item name="colorOnSurface">@color/design_dark_default_color_on_surface</item> + <item name="colorOnPrimarySurface">?attr/colorOnSurface</item> + + <item name="scrimBackground">@color/mtrl_scrim_color</item> + <item name="popupMenuBackground">@drawable/mtrl_popupmenu_background_overlay</item> + + <item name="minTouchTargetSize">@dimen/mtrl_min_touch_target_size</item> + + <!-- MaterialComponents Widget styles --> + <item name="badgeStyle">@style/Widget.MaterialComponents.Badge</item> + <item name="bottomAppBarStyle">@style/Widget.MaterialComponents.BottomAppBar</item> + <item name="chipStyle">@style/Widget.MaterialComponents.Chip.Action</item> + <item name="chipGroupStyle">@style/Widget.MaterialComponents.ChipGroup</item> + <item name="chipStandaloneStyle">@style/Widget.MaterialComponents.Chip.Entry</item> + <item name="circularProgressIndicatorStyle">@style/Widget.MaterialComponents.CircularProgressIndicator</item> + <item name="extendedFloatingActionButtonStyle">@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon</item> + <item name="linearProgressIndicatorStyle">@style/Widget.MaterialComponents.LinearProgressIndicator</item> + <item name="materialButtonStyle">@style/Widget.MaterialComponents.Button</item> + <item name="materialButtonOutlinedStyle">@style/Widget.MaterialComponents.Button.OutlinedButton</item> + <item name="materialButtonToggleGroupStyle">@style/Widget.MaterialComponents.MaterialButtonToggleGroup</item> + <item name="materialCardViewStyle">@style/Widget.MaterialComponents.CardView</item> + <item name="navigationRailStyle">@style/Widget.MaterialComponents.NavigationRailView</item> + <item name="sliderStyle">@style/Widget.MaterialComponents.Slider</item> + + <!-- Type styles --> + <item name="textAppearanceHeadline1">@style/TextAppearance.MaterialComponents.Headline1</item> + <item name="textAppearanceHeadline2">@style/TextAppearance.MaterialComponents.Headline2</item> + <item name="textAppearanceHeadline3">@style/TextAppearance.MaterialComponents.Headline3</item> + <item name="textAppearanceHeadline4">@style/TextAppearance.MaterialComponents.Headline4</item> + <item name="textAppearanceHeadline5">@style/TextAppearance.MaterialComponents.Headline5</item> + <item name="textAppearanceHeadline6">@style/TextAppearance.MaterialComponents.Headline6</item> + <item name="textAppearanceSubtitle1">@style/TextAppearance.MaterialComponents.Subtitle1</item> + <item name="textAppearanceSubtitle2">@style/TextAppearance.MaterialComponents.Subtitle2</item> + <item name="textAppearanceBody1">@style/TextAppearance.MaterialComponents.Body1</item> + <item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item> + <item name="textAppearanceCaption">@style/TextAppearance.MaterialComponents.Caption</item> + <item name="textAppearanceButton">@style/TextAppearance.MaterialComponents.Button</item> + <item name="textAppearanceOverline">@style/TextAppearance.MaterialComponents.Overline</item> + + <!-- Shape styles --> + <item name="shapeAppearanceSmallComponent"> + @style/ShapeAppearance.MaterialComponents.SmallComponent + </item> + <item name="shapeAppearanceMediumComponent"> + @style/ShapeAppearance.MaterialComponents.MediumComponent + </item> + <item name="shapeAppearanceLargeComponent"> + @style/ShapeAppearance.MaterialComponents.LargeComponent + </item> + + <!-- Motion --> + <item name="motionEasingStandard">@string/material_motion_easing_standard</item> + <item name="motionEasingEmphasized">@string/material_motion_easing_emphasized</item> + <item name="motionEasingDecelerated">@string/material_motion_easing_decelerated</item> + <item name="motionEasingAccelerated">@string/material_motion_easing_accelerated</item> + <item name="motionEasingLinear">@string/material_motion_easing_linear</item> + + <item name="motionDurationShort1">@integer/material_motion_duration_short_1</item> + <item name="motionDurationShort2">@integer/material_motion_duration_short_2</item> + <item name="motionDurationMedium1">@integer/material_motion_duration_medium_1</item> + <item name="motionDurationMedium2">@integer/material_motion_duration_medium_2</item> + <item name="motionDurationLong1">@integer/material_motion_duration_long_1</item> + <item name="motionDurationLong2">@integer/material_motion_duration_long_2</item> + + <item name="motionPath">@integer/material_motion_path</item> + + <!-- Elevation Overlays --> + <item name="elevationOverlayEnabled">true</item> + <item name="elevationOverlayColor">?attr/colorOnSurface</item> + + <!-- END Base.V14.Theme.MaterialComponents.Bridge --> + + <!-- START Base.V14.Theme.MaterialComponents --> + <!-- + ~ Only a subset of widget attributes being actually used are included here since there are + ~ too many of them and they need to be investigated on a case-by-case basis. + --> + + <!-- Framework, AppCompat, or Design Widget styles --> + <item name="appBarLayoutStyle">@style/Widget.MaterialComponents.AppBarLayout.Surface</item> + + <!-- END Base.V14.Theme.MaterialComponents --> + </style> + <style name="Base.ThemeOverlay.MaterialComponents.PlatformBridge" parent="Base.V31.ThemeOverlay.AppCompat.PlatformBridge" /> + <style name="ThemeOverlay.MaterialComponents.PlatformBridge" parent="Base.ThemeOverlay.AppCompat.PlatformBridge" /> +</resources> diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt index fb935591d9f0..53507fe46d1f 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt @@ -18,6 +18,7 @@ package com.android.settingslib.datastore import android.content.ContentResolver import android.content.Context +import android.net.Uri import android.provider.Settings.Global import android.provider.Settings.SettingNotFoundException @@ -29,6 +30,9 @@ import android.provider.Settings.SettingNotFoundException class SettingsGlobalStore private constructor(contentResolver: ContentResolver) : SettingsStore(contentResolver) { + override val uri: Uri + get() = Global.getUriFor("") + override val tag: String get() = "SettingsGlobalStore" diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt index bc375712e597..ca7fd7bb5f1e 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt @@ -18,6 +18,7 @@ package com.android.settingslib.datastore import android.content.ContentResolver import android.content.Context +import android.net.Uri import android.provider.Settings.Secure import android.provider.Settings.SettingNotFoundException @@ -29,6 +30,9 @@ import android.provider.Settings.SettingNotFoundException class SettingsSecureStore private constructor(contentResolver: ContentResolver) : SettingsStore(contentResolver) { + override val uri: Uri + get() = Secure.getUriFor("") + override val tag: String get() = "SettingsSecureStore" diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt index fdefa39dbd2e..62d3fc364d28 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt @@ -21,7 +21,6 @@ import android.database.ContentObserver import android.net.Uri import android.os.Handler import android.os.Looper -import android.provider.Settings import android.util.Log import java.util.concurrent.Executor import java.util.concurrent.atomic.AtomicInteger @@ -70,13 +69,12 @@ abstract class SettingsStore(protected val contentResolver: ContentResolver) : private fun onObserverAdded() { if (counter.getAndIncrement() != 0) return Log.i(tag, "registerContentObserver") - contentResolver.registerContentObserver( - Settings.Global.getUriFor(""), - true, - contentObserver, - ) + contentResolver.registerContentObserver(uri, true, contentObserver) } + /** The URI to watch for any key change. */ + protected abstract val uri: Uri + override fun removeObserver(observer: KeyedObserver<String?>) = if (super.removeObserver(observer)) { onObserverRemoved() @@ -99,6 +97,37 @@ abstract class SettingsStore(protected val contentResolver: ContentResolver) : contentResolver.unregisterContentObserver(contentObserver) } + /** Gets the boolean value of given key. */ + fun getBoolean(key: String): Boolean? = getValue(key, Boolean::class.javaObjectType) + + /** Sets boolean value for given key, null value means delete the key from data store. */ + fun setBoolean(key: String, value: Boolean?) = + setValue(key, Boolean::class.javaObjectType, value) + + /** Gets the float value of given key. */ + fun getFloat(key: String): Float? = getValue(key, Float::class.javaObjectType) + + /** Sets float value for given key, null value means delete the key from data store. */ + fun setFloat(key: String, value: Float?) = setValue(key, Float::class.javaObjectType, value) + + /** Gets the int value of given key. */ + fun getInt(key: String): Int? = getValue(key, Int::class.javaObjectType) + + /** Sets int value for given key, null value means delete the key from data store. */ + fun setInt(key: String, value: Int?) = setValue(key, Int::class.javaObjectType, value) + + /** Gets the long value of given key. */ + fun getLong(key: String): Long? = getValue(key, Long::class.javaObjectType) + + /** Sets long value for given key, null value means delete the key from data store. */ + fun setLong(key: String, value: Long?) = setValue(key, Long::class.javaObjectType, value) + + /** Gets the string value of given key. */ + fun getString(key: String): String? = getValue(key, String::class.javaObjectType) + + /** Sets string value for given key, null value means delete the key from data store. */ + fun setString(key: String, value: String?) = setValue(key, String::class.javaObjectType, value) + /** Tag for logging. */ abstract val tag: String } diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt index 1c75c7cac0c3..20a74d3b4a81 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt @@ -18,6 +18,7 @@ package com.android.settingslib.datastore import android.content.ContentResolver import android.content.Context +import android.net.Uri import android.provider.Settings.SettingNotFoundException import android.provider.Settings.System @@ -29,6 +30,9 @@ import android.provider.Settings.System class SettingsSystemStore private constructor(contentResolver: ContentResolver) : SettingsStore(contentResolver) { + override val uri: Uri + get() = System.getUriFor("") + override val tag: String get() = "SettingsSystemStore" diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java index ea4ac2c928ce..635f6905e4f0 100644 --- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java @@ -20,10 +20,13 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORE import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED; import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED; +import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; +import android.hardware.devicestate.DeviceStateManager; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; @@ -67,7 +70,8 @@ public final class DeviceStateRotationLockSettingsManager { @VisibleForTesting DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) { mSecureSettings = secureSettings; - mPosturesHelper = new PosturesHelper(context); + + mPosturesHelper = new PosturesHelper(context, getDeviceStateManager(context)); mPostureRotationLockDefaults = context.getResources() .getStringArray(R.array.config_perDeviceStateRotationLockDefaults); @@ -76,6 +80,14 @@ public final class DeviceStateRotationLockSettingsManager { listenForSettingsChange(); } + @Nullable + private DeviceStateManager getDeviceStateManager(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + return context.getSystemService(DeviceStateManager.class); + } + return null; + } + /** Returns a singleton instance of this class */ public static synchronized DeviceStateRotationLockSettingsManager getInstance(Context context) { if (sSingleton == null) { diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt index 6a13eb8c3907..14d59f2e416c 100644 --- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt @@ -17,6 +17,12 @@ package com.android.settingslib.devicestate import android.content.Context +import android.hardware.devicestate.DeviceState +import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN +import android.hardware.devicestate.DeviceStateManager import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY @@ -24,37 +30,68 @@ import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNKNOWN import android.provider.Settings.Secure.DeviceStateRotationLockKey import com.android.internal.R +import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags /** Helps to convert between device state and posture. */ -class PosturesHelper(context: Context) { +class PosturesHelper(context: Context, deviceStateManager: DeviceStateManager?) { - private val foldedDeviceStates = - context.resources.getIntArray(R.array.config_foldedDeviceStates) - private val halfFoldedDeviceStates = - context.resources.getIntArray(R.array.config_halfFoldedDeviceStates) - private val unfoldedDeviceStates = - context.resources.getIntArray(R.array.config_openDeviceStates) - private val rearDisplayDeviceStates = - context.resources.getIntArray(R.array.config_rearDisplayDeviceStates) + private val postures: Map<Int, List<Int>> + + init { + if (deviceStateManager != null && DeviceStateManagerFlags.deviceStatePropertyMigration()) { + postures = + deviceStateManager.supportedDeviceStates.groupBy { it.toPosture() } + .filterKeys { it != DEVICE_STATE_ROTATION_KEY_UNKNOWN } + .mapValues { it.value.map { it.identifier }} + } else { + val foldedDeviceStates = + context.resources.getIntArray(R.array.config_foldedDeviceStates).toList() + val halfFoldedDeviceStates = + context.resources.getIntArray(R.array.config_halfFoldedDeviceStates).toList() + val unfoldedDeviceStates = + context.resources.getIntArray(R.array.config_openDeviceStates).toList() + val rearDisplayDeviceStates = + context.resources.getIntArray(R.array.config_rearDisplayDeviceStates).toList() + + postures = + mapOf( + DEVICE_STATE_ROTATION_KEY_FOLDED to foldedDeviceStates, + DEVICE_STATE_ROTATION_KEY_HALF_FOLDED to halfFoldedDeviceStates, + DEVICE_STATE_ROTATION_KEY_UNFOLDED to unfoldedDeviceStates, + DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY to rearDisplayDeviceStates + ) + } + } @DeviceStateRotationLockKey fun deviceStateToPosture(deviceState: Int): Int { - return when (deviceState) { - in foldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_FOLDED - in halfFoldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_HALF_FOLDED - in unfoldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_UNFOLDED - in rearDisplayDeviceStates -> DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY - else -> DEVICE_STATE_ROTATION_KEY_UNKNOWN - } + return postures.filterValues { it.contains(deviceState) }.keys.firstOrNull() + ?: DEVICE_STATE_ROTATION_KEY_UNKNOWN } fun postureToDeviceState(@DeviceStateRotationLockKey posture: Int): Int? { - return when (posture) { - DEVICE_STATE_ROTATION_KEY_FOLDED -> foldedDeviceStates.firstOrNull() - DEVICE_STATE_ROTATION_KEY_HALF_FOLDED -> halfFoldedDeviceStates.firstOrNull() - DEVICE_STATE_ROTATION_KEY_UNFOLDED -> unfoldedDeviceStates.firstOrNull() - DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY -> rearDisplayDeviceStates.firstOrNull() - else -> null + return postures[posture]?.firstOrNull() + } + + /** + * Maps a [DeviceState] to the corresponding [DeviceStateRotationLockKey] value based on the + * properties of the state. + */ + @DeviceStateRotationLockKey + private fun DeviceState.toPosture(): Int { + return if (hasProperty(PROPERTY_FEATURE_REAR_DISPLAY)) { + DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY + } else if (hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)) { + DEVICE_STATE_ROTATION_KEY_FOLDED + } else if (hasProperties( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY, + PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN + )) { + DEVICE_STATE_ROTATION_KEY_HALF_FOLDED + } else if (hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) { + DEVICE_STATE_ROTATION_KEY_UNFOLDED + } else { + DEVICE_STATE_ROTATION_KEY_UNKNOWN } } } diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp index cd8f584953aa..12890feaf56e 100644 --- a/packages/SettingsLib/IllustrationPreference/Android.bp +++ b/packages/SettingsLib/IllustrationPreference/Android.bp @@ -21,6 +21,7 @@ android_library { "SettingsLibColor", "androidx.preference_preference", "lottie", + "SettingsLibSettingsTheme", "settingslib_illustrationpreference_flags_lib", ], diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java index a0599bb32dd1..adc4f316aca9 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java @@ -55,7 +55,7 @@ import java.io.InputStream; /** * IllustrationPreference is a preference that can play lottie format animation */ -public class IllustrationPreference extends Preference { +public class IllustrationPreference extends Preference implements GroupSectionDividerMixin { private static final String TAG = "IllustrationPreference"; diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml index e3f8fbb88a65..2e3ee32e2efb 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml @@ -20,8 +20,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingTop="@dimen/settingslib_switchbar_margin" android:paddingBottom="@dimen/settingslib_switchbar_margin" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml index 255b2c92e709..3e0e18488f36 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml @@ -20,8 +20,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingTop="@dimen/settingslib_switchbar_margin" android:paddingBottom="@dimen/settingslib_switchbar_margin" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml index 4425ef08d6e3..f75d9b23c49b 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml @@ -20,8 +20,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:paddingVertical="@dimen/settingslib_expressive_space_small1" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml index bf34db93298b..7c0eaeaca3de 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml @@ -18,11 +18,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" - android:layout_width="match_parent" - android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingRight="?android:attr/listPreferredItemPaddingRight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> + android:layout_width="match_parent"> <TextView android:id="@+id/switch_text" diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml index bef6e352d854..fa908a4ed6c8 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml @@ -18,6 +18,10 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" + android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingRight="?android:attr/listPreferredItemPaddingRight" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:importantForAccessibility="no"> <com.android.settingslib.widget.MainSwitchBar diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java index d895c874d95e..3394874797e3 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java @@ -36,7 +36,8 @@ import java.util.List; * This component is used as the main switch of the page * to enable or disable the prefereces on the page. */ -public class MainSwitchPreference extends TwoStatePreference implements OnCheckedChangeListener { +public class MainSwitchPreference extends TwoStatePreference + implements OnCheckedChangeListener, GroupSectionDividerMixin { private final List<OnCheckedChangeListener> mSwitchChangeListeners = new ArrayList<>(); diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt index 95b921b8e8c8..efa1faf6c485 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt @@ -20,8 +20,10 @@ import android.content.Context import android.os.Handler import android.os.Looper import androidx.preference.Preference +import androidx.preference.PreferenceDataStore import androidx.preference.PreferenceGroup import androidx.preference.PreferenceScreen +import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.datastore.KeyedDataObservable import com.android.settingslib.datastore.KeyedObservable import com.android.settingslib.datastore.KeyedObserver @@ -181,15 +183,22 @@ class PreferenceScreenBindingHelper( private fun PreferenceGroup.bindRecursively( preferenceBindingFactory: PreferenceBindingFactory, preferences: Map<String, PreferenceMetadata>, + storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(), ) { preferenceBindingFactory.bind(this, preferences[key]) val count = preferenceCount for (index in 0 until count) { val preference = getPreference(index) if (preference is PreferenceGroup) { - preference.bindRecursively(preferenceBindingFactory, preferences) + preference.bindRecursively(preferenceBindingFactory, preferences, storages) } else { - preferenceBindingFactory.bind(preference, preferences[preference.key]) + preferences[preference.key]?.let { + preferenceBindingFactory.getPreferenceBinding(it)?.bind(preference, it) + (it as? PersistentPreference<*>)?.storage(context)?.let { storage -> + preference.preferenceDataStore = + storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) } + } + } } } } diff --git a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt index e27838c4ef4d..89881f4d74bb 100644 --- a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt +++ b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt @@ -27,6 +27,7 @@ import androidx.preference.PreferenceScreen import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat +import java.util.concurrent.atomic.AtomicBoolean import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -36,7 +37,7 @@ import org.junit.runner.RunWith abstract class CatalystScreenTestCase { @get:Rule val setFlagsRule = SetFlagsRule() - protected val context: Context = ApplicationProvider.getApplicationContext() + protected val appContext: Context = ApplicationProvider.getApplicationContext() /** Catalyst screen. */ protected abstract val preferenceScreenCreator: PreferenceScreenCreator @@ -51,12 +52,12 @@ abstract class CatalystScreenTestCase { @Test open fun migration() { enableCatalystScreen() - assertThat(preferenceScreenCreator.isFlagEnabled(context)).isTrue() + assertThat(preferenceScreenCreator.isFlagEnabled(appContext)).isTrue() val catalystScreen = dumpPreferenceScreen() - Log.i("Catalyst", catalystScreen) + Log.i(TAG, catalystScreen) disableCatalystScreen() - assertThat(preferenceScreenCreator.isFlagEnabled(context)).isFalse() + assertThat(preferenceScreenCreator.isFlagEnabled(appContext)).isFalse() val legacyScreen = dumpPreferenceScreen() assertThat(catalystScreen).isEqualTo(legacyScreen) @@ -83,11 +84,26 @@ abstract class CatalystScreenTestCase { } private fun dumpPreferenceScreen(): String { + // Dump threads for troubleshooting when the test thread is stuck. + // Latest junit Timeout rule supports similar feature but it is not yet available on AOSP. + val taskFinished = AtomicBoolean() + Thread { + Thread.sleep(20000) + if (!taskFinished.get()) dumpThreads() + } + .apply { + isDaemon = true + start() + } + @Suppress("UNCHECKED_CAST") val clazz = preferenceScreenCreator.fragmentClass() as Class<PreferenceFragmentCompat> val builder = StringBuilder() FragmentScenario.launch(clazz).use { - it.onFragment { fragment -> fragment.preferenceScreen.toString(builder) } + it.onFragment { fragment -> + taskFinished.set(true) + fragment.preferenceScreen.toString(builder) + } } return builder.toString() } @@ -120,4 +136,16 @@ abstract class CatalystScreenTestCase { } builder.append(indent).append("}\n") } + + companion object { + const val TAG = "CatalystScreenTestCase" + + fun dumpThreads() { + for ((thread, stack) in Thread.getAllStackTraces()) { + Log.i(TAG, "$thread") + for (frame in stack) Log.i(TAG, " $frame") + Log.i(TAG, "") + } + } + } } diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt new file mode 100644 index 000000000000..ba5f5cf855be --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget + +/** + * A base interface to indicate that a class should not have rounded corners. + * + * Classes implementing this interface will be treated as already handle rounded corners. + */ +interface GroupSectionDividerMixin
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt new file mode 100644 index 000000000000..535d80f609fb --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget + +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceScreen +import androidx.recyclerview.widget.RecyclerView + +/** Base class for Settings to use PreferenceFragmentCompat */ +open abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() { + + override fun onCreateAdapter(preferenceScreen: PreferenceScreen): RecyclerView.Adapter<*> { + if (SettingsThemeHelper.isExpressiveTheme(requireContext())) + return SettingsPreferenceGroupAdapter(preferenceScreen) + return super.onCreateAdapter(preferenceScreen) + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt new file mode 100644 index 000000000000..98b7f7664b1b --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget + +import android.os.Handler +import android.os.Looper +import androidx.annotation.DrawableRes +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceGroup +import androidx.preference.PreferenceGroupAdapter +import androidx.preference.PreferenceViewHolder +import com.android.settingslib.widget.theme.R + +/** + * A custom adapter for displaying settings preferences in a list, handling rounded corners + * for preference items within a group. + */ +open class SettingsPreferenceGroupAdapter @JvmOverloads constructor( + preferenceGroup: PreferenceGroup +) : PreferenceGroupAdapter(preferenceGroup) { + + private val mPreferenceGroup = preferenceGroup + private var mRoundCornerMappingList: ArrayList<Int> = ArrayList() + + private var mNormalPaddingStart = 0 + private var mGroupPaddingStart = 0 + private var mNormalPaddingEnd = 0 + private var mGroupPaddingEnd = 0 + + private val mHandler = Handler(Looper.getMainLooper()) + + private val syncRunnable = Runnable { updatePreferences() } + + init { + val context = preferenceGroup.context + mNormalPaddingStart = + context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1) + mGroupPaddingStart = mNormalPaddingStart * 2 + mNormalPaddingEnd = + context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1) + mGroupPaddingEnd = mNormalPaddingEnd * 2 + updatePreferences() + } + + override fun onPreferenceHierarchyChange(preference: Preference) { + super.onPreferenceHierarchyChange(preference) + + // Post after super class has posted their sync runnable to update preferences. + mHandler.removeCallbacks(syncRunnable) + mHandler.post(syncRunnable) + } + + override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) { + super.onBindViewHolder(holder, position) + updateBackground(holder, position) + } + + private fun updatePreferences() { + val oldList = ArrayList(mRoundCornerMappingList) + mRoundCornerMappingList = ArrayList() + mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup) + if (mRoundCornerMappingList != oldList) { + notifyDataSetChanged() + } + } + + private fun mappingPreferenceGroup(cornerStyles: MutableList<Int>, group: PreferenceGroup) { + cornerStyles.clear() + cornerStyles.addAll(MutableList(itemCount) { 0 }) + + // the first item in to group + var startIndex = -1 + // the last item in the group + var endIndex = -1 + var currentParent: PreferenceGroup? = group + for (i in 0 until itemCount) { + when (val pref = getItem(i)) { + // the preference has round corner background, so we don't need to handle it. + is GroupSectionDividerMixin -> { + cornerStyles[i] = 0 + startIndex = -1 + endIndex = -1 + } + + // PreferenceCategory should not have round corner background. + is PreferenceCategory -> { + cornerStyles[i] = 0 + startIndex = -1 + endIndex = -1 + currentParent = pref + } + + // ExpandablePreference is PreferenceGroup but it should handle round corner + is Expandable -> { + // When ExpandablePreference is expanded, we treat is as the first item. + if (pref.isExpanded()) { + currentParent = pref as? PreferenceGroup + startIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP or ROUND_CORNER_CENTER + endIndex = -1 + } + } + + else -> { + val parent = pref?.parent + + // item in the group should have round corner background. + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_CENTER + if (parent === currentParent) { + // find the first item in the group + if (startIndex == -1) { + startIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP + } + + // find the last item in the group, if we find the new last item, we should + // remove the old last item round corner. + if (endIndex == -1 || endIndex < i) { + if (endIndex != -1) { + cornerStyles[endIndex] = + cornerStyles[endIndex] and ROUND_CORNER_BOTTOM.inv() + } + endIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM + } + } else { + // this item is new group, we should reset the index. + currentParent = parent + startIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP + endIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM + } + } + } + } + } + + /** handle roundCorner background */ + private fun updateBackground(holder: PreferenceViewHolder, position: Int) { + @DrawableRes val backgroundRes = getRoundCornerDrawableRes(position, false /* isSelected*/) + + val v = holder.itemView + val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart + val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd + + v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom) + v.setBackgroundResource(backgroundRes) + } + + @DrawableRes + protected fun getRoundCornerDrawableRes(position: Int, isSelected: Boolean): Int { + val cornerType = mRoundCornerMappingList[position] + + if ((cornerType and ROUND_CORNER_CENTER) == 0) { + return 0 + } + + return when { + (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) == 0 -> { + // the first + if (isSelected) R.drawable.settingslib_round_background_top_selected + else R.drawable.settingslib_round_background_top + } + + (cornerType and ROUND_CORNER_BOTTOM) != 0 && (cornerType and ROUND_CORNER_TOP) == 0 -> { + // the last + if (isSelected) R.drawable.settingslib_round_background_bottom_selected + else R.drawable.settingslib_round_background_bottom + } + + (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) != 0 -> { + // the only one preference + if (isSelected) R.drawable.settingslib_round_background_selected + else R.drawable.settingslib_round_background + } + + else -> { + // in the center + if (isSelected) R.drawable.settingslib_round_background_center_selected + else R.drawable.settingslib_round_background_center + } + } + } + + companion object { + private const val ROUND_CORNER_CENTER: Int = 1 + private const val ROUND_CORNER_TOP: Int = 1 shl 1 + private const val ROUND_CORNER_BOTTOM: Int = 1 shl 2 + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt index dfd296fe006f..8636524ed23c 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt @@ -23,6 +23,7 @@ import com.android.settingslib.spa.framework.common.SpaEnvironment import com.android.settingslib.spa.framework.common.createSettingsPage import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider import com.android.settingslib.spa.gallery.banner.BannerPageProvider +import com.android.settingslib.spa.gallery.card.CardPageProvider import com.android.settingslib.spa.gallery.chart.ChartPageProvider import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider import com.android.settingslib.spa.gallery.dialog.NavDialogProvider @@ -109,6 +110,7 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) { TopIntroPreferencePageProvider, CheckBoxPreferencePageProvider, TwoTargetButtonPreferencePageProvider, + CardPageProvider, ), rootPages = listOf( HomePageProvider.createSettingsPage(), diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt new file mode 100644 index 000000000000..5659e2f33156 --- /dev/null +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.gallery.card + +import android.os.Bundle +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Stars +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.tooling.preview.Preview +import com.android.settingslib.spa.framework.common.SettingsPageProvider +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.widget.card.SuggestionCard +import com.android.settingslib.spa.widget.card.SuggestionCardModel +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spa.widget.scaffold.RegularScaffold + +object CardPageProvider : SettingsPageProvider { + override val name = "Card" + + override fun getTitle(arguments: Bundle?) = TITLE + + @Composable + override fun Page(arguments: Bundle?) { + RegularScaffold(title = TITLE) { + SuggestionCard() + SuggestionCardWithLongTitle() + SuggestionCardDismissible() + } + } + + @Composable + private fun SuggestionCard() { + SuggestionCard( + SuggestionCardModel( + title = "Suggestion card", + description = "Suggestion card description", + imageVector = Icons.Filled.Stars, + ) + ) + } + + @Composable + private fun SuggestionCardWithLongTitle() { + SuggestionCard( + SuggestionCardModel( + title = "Top level suggestion card with a really, really long title", + imageVector = Icons.Filled.Stars, + onClick = {}, + ) + ) + } + + @Composable + private fun SuggestionCardDismissible() { + var isVisible by rememberSaveable { mutableStateOf(true) } + SuggestionCard( + SuggestionCardModel( + title = "Suggestion card", + description = "Suggestion card description", + imageVector = Icons.Filled.Stars, + onDismiss = { isVisible = false }, + isVisible = isVisible, + ) + ) + } + + @Composable + fun Entry() { + Preference( + object : PreferenceModel { + override val title = TITLE + override val onClick = navigator(name) + } + ) + } + + private const val TITLE = "Sample Card" +} + +@Preview +@Composable +private fun CardPagePreview() { + SettingsTheme { CardPageProvider.Page(null) } +} diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt index 4d77ea173a85..ebfc0c536868 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt @@ -27,6 +27,7 @@ import com.android.settingslib.spa.gallery.R import com.android.settingslib.spa.gallery.SettingsPageProviderEnum import com.android.settingslib.spa.gallery.banner.BannerPageProvider import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider +import com.android.settingslib.spa.gallery.card.CardPageProvider import com.android.settingslib.spa.gallery.chart.ChartPageProvider import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider @@ -80,6 +81,7 @@ object HomePageProvider : SettingsPageProvider { DialogMainPageProvider.Entry() EditorMainPageProvider.Entry() BannerPageProvider.Entry() + CardPageProvider.Entry() CopyablePageProvider.Entry() } } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt index 395748384b85..08bedf99519d 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt @@ -28,6 +28,7 @@ object SettingsDimension { val paddingExtraSmall6 = 12.dp val paddingLarge = 16.dp val paddingExtraLarge = 24.dp + val paddingExtraLarge1 = 28.dp val spinnerHorizontalPadding = paddingExtraLarge val spinnerVerticalPadding = paddingLarge @@ -37,6 +38,7 @@ object SettingsDimension { val actionIconPadding = 4.dp val itemIconSize = 24.dp + val itemIconContainerSizeSmall = 40.dp val itemIconContainerSize = 72.dp val itemPaddingStart = if (isSpaExpressiveEnabled) paddingLarge else paddingExtraLarge val itemPaddingEnd = paddingLarge @@ -47,6 +49,12 @@ object SettingsDimension { end = itemPaddingEnd, bottom = itemPaddingVertical, ) + val footerItemPadding = PaddingValues( + start = paddingExtraLarge1, + top = itemPaddingVertical, + end = itemPaddingEnd, + bottom = itemPaddingVertical, + ) val textFieldPadding = PaddingValues( start = itemPaddingStart, end = itemPaddingEnd, diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt index 86ba6864574c..61607bc8ae8a 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt @@ -29,4 +29,6 @@ object SettingsShape { val CornerLarge = RoundedCornerShape(24.dp) val CornerExtraLarge = RoundedCornerShape(28.dp) + + val CornerExtraLarge1 = RoundedCornerShape(40.dp) } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SuggestionCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SuggestionCard.kt new file mode 100644 index 000000000000..2126634ebd4d --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SuggestionCard.kt @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.widget.card + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Stars +import androidx.compose.material.icons.outlined.Stars +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.tooling.preview.Preview +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.SettingsShape +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.framework.theme.toSemiBoldWeight + +data class SuggestionCardModel( + val title: String, + val description: String? = null, + val imageVector: ImageVector, + + /** + * A dismiss button will be displayed if this is not null. + * + * And this callback will be called when user clicks the button. + */ + val onDismiss: (() -> Unit)? = null, + val isVisible: Boolean = true, + val onClick: (() -> Unit)? = null, +) + +@Composable +fun SuggestionCard(model: SuggestionCardModel) { + AnimatedVisibility(visible = model.isVisible) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = + Modifier.padding( + horizontal = SettingsDimension.paddingLarge, + vertical = SettingsDimension.paddingSmall, + ) + .fillMaxWidth() + .heightIn(min = SettingsDimension.preferenceMinHeight) + .clip(SettingsShape.CornerExtraLarge1) + .background(MaterialTheme.colorScheme.secondaryContainer) + .then(model.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier) + .padding(SettingsDimension.paddingExtraSmall6), + ) { + SuggestionCardIcon(model.imageVector) + Spacer(Modifier.padding(SettingsDimension.paddingSmall)) + Column(modifier = Modifier.weight(1f).semantics(mergeDescendants = true) {}) { + SuggestionCardTitle(model.title) + if (model.description != null) SuggestionCardDescription(model.description) + } + if (model.onDismiss != null) { + Spacer(Modifier.padding(SettingsDimension.paddingSmall)) + SuggestionCardDismissButton(model.onDismiss) + } + } + } +} + +@Composable +private fun SuggestionCardIcon(imageVector: ImageVector) { + Box( + modifier = + Modifier.padding(SettingsDimension.paddingSmall) + .size(SettingsDimension.itemIconContainerSizeSmall) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.secondary), + contentAlignment = Alignment.Center, + ) { + Icon( + imageVector = imageVector, + contentDescription = null, + modifier = Modifier.size(SettingsDimension.itemIconSize), + tint = MaterialTheme.colorScheme.onSecondary, + ) + } +} + +@Composable +private fun SuggestionCardTitle(title: String) { + Text( + text = title, + style = MaterialTheme.typography.titleMedium.toSemiBoldWeight(), + modifier = Modifier.padding(vertical = SettingsDimension.paddingTiny), + color = MaterialTheme.colorScheme.onSecondaryContainer, + ) +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +private fun SuggestionCardDescription(description: String) { + Text( + text = description, + style = MaterialTheme.typography.bodySmallEmphasized, + modifier = Modifier.padding(vertical = SettingsDimension.paddingTiny), + color = MaterialTheme.colorScheme.onSecondaryContainer, + ) +} + +@Composable +private fun SuggestionCardDismissButton(onDismiss: () -> Unit) { + IconButton(shape = CircleShape, onClick = onDismiss) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = + stringResource(androidx.compose.material3.R.string.m3c_snackbar_dismiss), + modifier = Modifier.size(SettingsDimension.itemIconSize), + tint = MaterialTheme.colorScheme.onSecondaryContainer, + ) + } +} + +@Preview +@Composable +private fun SuggestionCardPreview() { + SettingsTheme { + SuggestionCard( + SuggestionCardModel( + title = "Suggestion card", + description = "Suggestion card description", + imageVector = Icons.Outlined.Stars, + onDismiss = {}, + onClick = {}, + ) + ) + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt index acbdec0b30aa..66680fa547b1 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt @@ -49,7 +49,9 @@ fun CategoryTitle(title: String) { text = title, modifier = Modifier.padding( - start = SettingsDimension.itemPaddingStart, + start = + if (isSpaExpressiveEnabled) SettingsDimension.paddingSmall + else SettingsDimension.itemPaddingStart, top = 20.dp, end = if (isSpaExpressiveEnabled) SettingsDimension.paddingSmall @@ -67,16 +69,16 @@ fun CategoryTitle(title: String) { */ @Composable fun Category(title: String? = null, content: @Composable ColumnScope.() -> Unit) { + var displayTitle by remember { mutableStateOf(false) } Column( modifier = - if (isSpaExpressiveEnabled) + if (isSpaExpressiveEnabled && displayTitle) Modifier.padding( horizontal = SettingsDimension.paddingLarge, vertical = SettingsDimension.paddingSmall, ) else Modifier ) { - var displayTitle by remember { mutableStateOf(false) } if (title != null && displayTitle) CategoryTitle(title = title) Column( modifier = diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt index a9d2ef6dc3f6..6e4fd78a039b 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt @@ -101,8 +101,8 @@ fun Spinner(options: List<SpinnerOption>, selectedId: Int?, setId: (id: Int) -> Modifier.background(MaterialTheme.colorScheme.surfaceContainerLow) .padding(horizontal = SettingsDimension.paddingSmall), ) { - for ((index, option) in options.withIndex()) { - val selected = index + 1 == selectedId + for (option in options) { + val selected = option.id == selectedId DropdownMenuItem( text = { SpinnerOptionText(option = option, selected) }, onClick = { diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsBannerTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsBannerTest.kt index a8479b01a861..a8479b01a861 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsBannerTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsBannerTest.kt diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleBannerTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsCollapsibleBannerTest.kt index 1080fdea9455..1080fdea9455 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleBannerTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsCollapsibleBannerTest.kt diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SuggestionCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SuggestionCardTest.kt new file mode 100644 index 000000000000..96bfb3d71642 --- /dev/null +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SuggestionCardTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.widget.card + +import android.content.Context +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Star +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.isNotDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SuggestionCardTest { + @get:Rule val composeTestRule = createComposeRule() + + private val context: Context = ApplicationProvider.getApplicationContext() + + @Test + fun suggestionCard_contentDisplayed() { + setContent() + + composeTestRule.onNodeWithText(TITLE).assertIsDisplayed() + composeTestRule.onNodeWithText(DESCRIPTION).assertIsDisplayed() + } + + @Test + fun suggestionCard_dismiss() { + setContent() + composeTestRule + .onNodeWithContentDescription( + context.getString(androidx.compose.material3.R.string.m3c_snackbar_dismiss) + ) + .performClick() + + composeTestRule.onNodeWithText(TITLE).isNotDisplayed() + composeTestRule.onNodeWithText(DESCRIPTION).isNotDisplayed() + } + + private fun setContent() { + composeTestRule.setContent { + var isVisible by rememberSaveable { mutableStateOf(true) } + SuggestionCard( + SuggestionCardModel( + title = TITLE, + description = DESCRIPTION, + imageVector = Icons.Outlined.Star, + isVisible = isVisible, + onDismiss = { isVisible = false }, + ) + ) + } + } + + private companion object { + const val TITLE = "Title" + const val DESCRIPTION = "Description" + } +} diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt index f306918ec72f..d89d3977cac3 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt @@ -39,6 +39,8 @@ import androidx.compose.ui.unit.Dp import com.android.settingslib.development.DevelopmentSettingsEnabler import com.android.settingslib.spa.framework.compose.rememberDrawablePainter import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled +import com.android.settingslib.spa.widget.preference.IntroAppPreference import com.android.settingslib.spa.widget.ui.CopyableBody import com.android.settingslib.spa.widget.ui.SettingsBody import com.android.settingslib.spa.widget.ui.SettingsTitle @@ -48,23 +50,53 @@ import com.android.settingslib.spaprivileged.model.app.rememberAppRepository class AppInfoProvider(private val packageInfo: PackageInfo) { @Composable fun AppInfo(displayVersion: Boolean = false, isClonedAppPage: Boolean = false) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = SettingsDimension.itemPaddingStart, - vertical = SettingsDimension.itemPaddingVertical, - ) - .semantics(mergeDescendants = true) {}, - horizontalAlignment = Alignment.CenterHorizontally, - ) { + if (isSpaExpressiveEnabled) { + val appRepository = rememberAppRepository() val app = checkNotNull(packageInfo.applicationInfo) - Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) { - AppIcon(app = app, size = SettingsDimension.appIconInfoSize) + val title = appRepository.produceLabel(app, isClonedAppPage).value + + val descriptions = mutableListOf<String>() + if (app.isInstantApp) { + descriptions.add( + stringResource( + com.android.settingslib.widget.preference.app.R.string.install_type_instant + ) + ) + } + if (displayVersion) { + val versionName = packageInfo.versionNameBidiWrapped + if (versionName != null) descriptions.add(versionName) + } + + IntroAppPreference( + title = title, + descriptions = descriptions, + appIcon = { + Image( + painter = rememberDrawablePainter(appRepository.produceIcon(app).value), + contentDescription = appRepository.produceIconContentDescription(app).value, + ) + }, + ) + } else { + Column( + modifier = + Modifier.fillMaxWidth() + .padding( + horizontal = SettingsDimension.itemPaddingStart, + vertical = SettingsDimension.itemPaddingVertical, + ) + .semantics(mergeDescendants = true) {}, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + val app = checkNotNull(packageInfo.applicationInfo) + Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) { + AppIcon(app = app, size = SettingsDimension.appIconInfoSize) + } + AppLabel(app, isClonedAppPage) + InstallType(app) + if (displayVersion) AppVersion() } - AppLabel(app, isClonedAppPage) - InstallType(app) - if (displayVersion) AppVersion() } } @@ -89,19 +121,24 @@ class AppInfoProvider(private val packageInfo: PackageInfo) { @Composable fun FooterAppVersion(showPackageName: Boolean = rememberIsDevelopmentSettingsEnabled()) { val context = LocalContext.current - val footer = remember(packageInfo, showPackageName) { - val list = mutableListOf<String>() - packageInfo.versionNameBidiWrapped?.let { - list += context.getString(R.string.version_text, it) + val footer = + remember(packageInfo, showPackageName) { + val list = mutableListOf<String>() + packageInfo.versionNameBidiWrapped?.let { + list += context.getString(R.string.version_text, it) + } + if (showPackageName) { + list += packageInfo.packageName + } + list.joinToString(separator = System.lineSeparator()) } - if (showPackageName) { - list += packageInfo.packageName - } - list.joinToString(separator = System.lineSeparator()) - } if (footer.isBlank()) return HorizontalDivider() - Column(modifier = Modifier.padding(SettingsDimension.itemPadding)) { + Column( + modifier = + if (isSpaExpressiveEnabled) Modifier.padding(SettingsDimension.footerItemPadding) + else Modifier.padding(SettingsDimension.itemPadding) + ) { CopyableBody(footer) } } @@ -109,9 +146,7 @@ class AppInfoProvider(private val packageInfo: PackageInfo) { @Composable private fun rememberIsDevelopmentSettingsEnabled(): Boolean { val context = LocalContext.current - return remember { - DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context) - } + return remember { DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context) } } private companion object { diff --git a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt index 9b1ccef9dadf..62573fe18357 100644 --- a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt +++ b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt @@ -32,7 +32,7 @@ class ZeroStatePreference @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin { private val iconTint: Int = context.getColor( com.android.settingslib.widget.theme.R.color.settingslib_materialColorOnSecondaryContainer diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index 79c3ff9ce989..5a8763f4db6e 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -149,3 +149,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "audio_sharing_developer_option" + namespace: "cross_device_experiences" + description: "Gates whether to enable audio sharing developer option" + bug: "368401233" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 6ee3bd16148e..2f158c88305a 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Jou toestel moet herselflaai om hierdie verandering toe te pas. Herselflaai nou of kanselleer."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Bedrade oorfoon"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Oorfoon"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-oudio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoonsok"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofoon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofoon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aan"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 40c428895c68..1040f377c6cd 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"የእርስዎን መሣሪያ ይህ ለው ለማመልከት እንደገና መነሣት አለበት። አሁን እንደገና ያስነሡ ወይም ይተዉት።"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ባለገመድ የራስ ላይ ማዳመጫ"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"የጆሮ ማዳመጫ"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ኦዲዮ"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"የማይክሮፎን መሰኪያ"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ማይክሮፎን"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ብሉቱዝ ማይክሮፎን"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"አብራ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index b54b52198c3a..9e1db5fbf36d 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"يجب إعادة تشغيل جهازك ليتم تطبيق هذا التغيير. يمكنك إعادة التشغيل الآن أو إلغاء التغيير."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"سماعات رأس سلكية"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"سماعات رأس"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"مكبر صوت USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"مقبس الميكروفون"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ميكروفون USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ميكروفون يعمل بالبلوتوث"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"مفعّلة"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index a72c5f61d28e..76a39368f7fd 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"সক্ষম কৰা আছে"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই সলনিটো কার্যকৰী হ’বলৈ আপোনাৰ ডিভাইচটো ৰিবুট কৰিবই লাগিব। এতিয়াই ৰিবুট কৰক অথবা বাতিল কৰক।"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"তাঁৰযুক্ত হেডফ’ন"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"হেডফ’ন"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ইউএছবি অডিঅ\'"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"মাইকৰ জেক"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ইউএছবি মাইক্ৰ’ফ’ন"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ব্লুটুথ মাইক্ৰ’ফ’ন"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"অন"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 4cb9ef8ce0ed..2d9e1d9fb726 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu dəyişikliyin tətbiq edilməsi üçün cihaz yenidən başladılmalıdır. İndi yenidən başladın və ya ləğv edin."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Naqilli qulaqlıq"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Qulaqlıq"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon yuvası"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofonu"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktiv"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 43281924b93c..01f0ff26eddc 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate da restartujete uređaj da bi se ova promena primenila. Restartujte ga odmah ili otkažite."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalice"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Utikač za mikrofon"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključeno"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 40be0a0f0392..f710b1d76028 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Уключана"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Перазагрузіце прыладу, каб прымяніць гэта змяненне. Перазагрузіце ці скасуйце."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Правадныя навушнікі"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Навушнікі"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Аўдыяпрылада USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Раздым для мікрафона"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Мікрафон USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Мікрафон з Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Уключана"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index f43758d5b5f5..c5c4c914a919 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Активирано"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да бъде приложена тази промяна, устройството ви трябва да бъде рестартирано. Рестартирайте сега или анулирайте."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Слушалки с кабел"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Слушалки"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Аудиоустройство с USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Жак за микрофон"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Микрофон с USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Микрофон с Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Включване"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index acc8a2db415c..be6b9be9887c 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই পরিবর্তনটি প্রয়োগ করার জন্য আপনার ডিভাইসটি অবশ্যই রিবুট করতে হবে। এখনই রিবুট করুন বা বাতিল করুন।"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"তারযুক্ত হেডফোন"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"হেডফোন"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB অডিও"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"মাইকের জ্যাক"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB মাইক্রোফোন"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT মাইক্রোফোন"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"চালু আছে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 99c0c9ecb02a..f34343050579 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate ponovo pokrenuti uređaj da se ova promjena primijeni. Ponovo pokrenite odmah ili otkažite."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalice"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Priključak za mikrofon"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključi"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index c9a14117b44e..3557b10c764b 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Has de reiniciar el teu dispositiu perquè s\'apliquin els canvis. Reinicia\'l ara o cancel·la."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculars amb cable"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auriculars"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Àudio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Connector per al micròfon"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micròfon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micròfon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activa"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index ee5fd5ce8059..ae47c4677da1 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aby se tato změna projevila, je třeba zařízení restartovat. Restartujte zařízení nebo zrušte akci."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kabelová sluchátka"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Sluchátka"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvuk USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Konektor mikrofonu"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Zapnout"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index a7308f1c09c0..82d702124a5a 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Din enhed skal genstartes for at anvende denne ændring. Genstart nu, eller annuller."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Høretelefoner med ledning"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Høretelefoner"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-lydenhed"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Stik til mikrofon"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Til"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 972bb1fea866..00ae7e48fd72 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -686,12 +686,13 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiviert"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Damit diese Änderung übernommen wird, musst du dein Gerät neu starten. Du kannst es jetzt neu starten oder den Vorgang abbrechen."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kabelgebundene Kopfhörer"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kopfhörer"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-Audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonanschluss"</string> - <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-Mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> <skip /> + <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-Mikrofon"</string> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-Mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"An"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Aus"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mobilfunknetzwerk wird gewechselt"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 2d0f37b41efb..7dbe27485491 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ενεργή"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Για να εφαρμοστεί αυτή η αλλαγή, θα πρέπει να επανεκκινήσετε τη συσκευή σας. Επανεκκίνηση τώρα ή ακύρωση."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Ενσύρματα ακουστικά"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Ακουστικά"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Ήχος USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Υποδοχή μικροφώνου"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Μικρόφωνο USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Μικρόφωνο BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ενεργό"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 96c844d95438..f29a02bc21e4 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index c07bd342e25b..81489fe4cd65 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -686,9 +686,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphone"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Wired audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Wired microphone"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 96c844d95438..f29a02bc21e4 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 96c844d95438..f29a02bc21e4 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index ec88743d32d9..07b8d88a95d5 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Debes reiniciar el dispositivo para que se aplique el cambio. Reinícialo ahora o cancela la acción."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculares con cable"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auriculares"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector para micrófono"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activar"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 6215e7729f7a..56355b1059ce 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -686,12 +686,13 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Es necesario reiniciar tu dispositivo para que se apliquen los cambios. Reinicia ahora o cancela la acción."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculares con cable"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auriculares"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector jack para micrófono"</string> - <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> <skip /> + <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activado"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivado"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambiando la red del operador"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 77a0799ca1ac..6582864e2e32 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Selle muudatuse rakendamiseks tuleb seade taaskäivitada. Taaskäivitage kohe või tühistage."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Juhtmega kõrvaklapid"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kõrvaklapid"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-heli"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoni pistikupesa"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Sees"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index a490b7b6a515..f9ec6f715448 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aldaketa aplikatzeko, berrabiarazi egin behar da gailua. Berrabiaraz ezazu orain, edo utzi bertan behera."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Entzungailu kableduna"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Entzungailua"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB bidezko audioa"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonoaren konektorea"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB bidezko mikrofonoa"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth bidezko mikrofonoa"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktibatu"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 305fcbeeef43..9c704c5b89eb 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"برای اعمال این تغییر، دستگاه باید بازراهاندازی شود. یا اکنون بازراهاندازی کنید یا لغو کنید."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"هدفون سیمی"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"هدفون"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"بلندگوی USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"فیش میکروفون"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"میکروفون USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"میکروفون بلوتوث"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"روشن"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index fc6d5da1250c..193ec7065086 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Laitteesi on käynnistettävä uudelleen, jotta muutos tulee voimaan. Käynnistä uudelleen nyt tai peru."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Langalliset kuulokkeet"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kuulokkeet"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoniliitäntä"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofoni"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofoni"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Päällä"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 05157b71d17a..b527990c5221 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -393,7 +393,7 @@ <string name="disable_overlays_summary" msgid="1954852414363338166">"Toujours utiliser le GPU pour la composition écran"</string> <string name="simulate_color_space" msgid="1206503300335835151">"Simuler espace colorimétrique"</string> <string name="enable_opengl_traces_title" msgid="4638773318659125196">"Enable OpenGL traces"</string> - <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Désact. routage audio USB"</string> + <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Désactiver le routage audio USB"</string> <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"Désactiver routage automatique appareils audio USB"</string> <string name="debug_layout" msgid="1659216803043339741">"Afficher les contours"</string> <string name="debug_layout_summary" msgid="8825829038287321978">"Afficher les limites, les marges de clip, etc."</string> @@ -686,12 +686,13 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Votre appareil doit être redémarré pour que ce changement prenne effet. Redémarrez-le maintenant ou annulez la modification."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Écouteurs filaires"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Écouteurs"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio par USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Prise du microphone"</string> - <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microphone USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> <skip /> + <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microphone USB"</string> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microphone BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activé"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Désactivé"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Changer de réseau de fournisseur de services"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 31616a79f79b..fb2bcf322edb 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -686,12 +686,13 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Vous devez redémarrer l\'appareil pour que cette modification soit appliquée. Redémarrez maintenant ou annulez l\'opération."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Casque filaire"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Casque audio"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Connecteur micro"</string> - <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micro USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> <skip /> + <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micro USB"</string> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micro Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Allumé"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Éteint"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Modification du réseau de l\'opérateur"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 25d4eb8ca8ec..d6e9f3fb4210 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necesario reiniciar o teu dispositivo para aplicar este cambio. Reiníciao agora ou cancela o cambio."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculares con cable"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auriculares"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector do micrófono"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activada"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index dad0ce543d88..b804dd4ef8cb 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ચાલુ છે"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"આ ફેરફારને લાગુ કરવા માટે તમારા ડિવાઇસને રીબૂટ કરવાની જરૂર છે. હમણાં જ રીબૂટ કરો કે રદ કરો."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"વાયરવાળો હૅડફોન"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"હૅડફોન"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ઑડિયો"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"માઇક જૅક"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB માઇક્રોફોન"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT માઇક્રોફોન"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ચાલુ"</string> diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml index d023dc7ce9a4..7a22465f80e5 100644 --- a/packages/SettingsLib/res/values-hi/arrays.xml +++ b/packages/SettingsLib/res/values-hi/arrays.xml @@ -207,7 +207,7 @@ <string-array name="window_animation_scale_entries"> <item msgid="2675263395797191850">"एनिमेशन बंद"</item> <item msgid="5790132543372767872">"एनिमेशन स्केल .5x"</item> - <item msgid="2529692189302148746">"एनिमेशन स्केल 1x"</item> + <item msgid="2529692189302148746">"ऐनिमेशन स्केल 1 गुना"</item> <item msgid="8072785072237082286">"एनिमेशन स्केल 1.5x"</item> <item msgid="3531560925718232560">"एनिमेशन स्केल 2x"</item> <item msgid="4542853094898215187">"एनिमेशन स्केल 5x"</item> @@ -225,7 +225,7 @@ <string-array name="animator_duration_scale_entries"> <item msgid="6416998593844817378">"एनिमेशन बंद"</item> <item msgid="875345630014338616">"एनिमेशन स्केल .5x"</item> - <item msgid="2753729231187104962">"एनिमेशन स्केल 1x"</item> + <item msgid="2753729231187104962">"ऐनिमेशन स्केल 1 गुना"</item> <item msgid="1368370459723665338">"एनिमेशन स्केल 1.5x"</item> <item msgid="5768005350534383389">"एनिमेशन स्केल 2x"</item> <item msgid="3728265127284005444">"एनिमेशन स्केल 5x"</item> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index fcfe9a34815e..7c5e66ccaf03 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -288,7 +288,7 @@ <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"OEM अनलॉक करने की अनुमति दें?"</string> <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"चेतावनी: इस सेटिंग के चालू रहने पर डिवाइस सुरक्षा सुविधाएं इस डिवाइस पर काम नहीं करेंगी."</string> <string name="mock_location_app" msgid="6269380172542248304">"मॉक लोकेशन के लिए ऐप्लिकेशन चुनें"</string> - <string name="mock_location_app_not_set" msgid="6972032787262831155">"मॉक लोकेशन के लिए ऐप्लिकेशन सेट नहीं है"</string> + <string name="mock_location_app_not_set" msgid="6972032787262831155">"मॉक लोकेशन के लिए कोई ऐप्लिकेशन सेट नहीं है"</string> <string name="mock_location_app_set" msgid="4706722469342913843">"मॉक लोकेशन के लिए ऐप्लिकेशन: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="debug_networking_category" msgid="6829757985772659599">"नेटवर्किंग"</string> <string name="wifi_display_certification" msgid="1805579519992520381">"वायरलेस डिसप्ले सर्टिफ़िकेशन"</string> @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"चालू है"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"बदली गई सेटिंग को लागू करने के लिए, डिवाइस को रीस्टार्ट करना होगा. अपने डिवाइस को रीस्टार्ट करें या रद्द करें."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"तार वाला हेडफ़ोन"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"हेडफ़ोन"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"यूएसबी ऑडियो"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइक्रोफ़ोन जैक"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"यूएसबी माइक्रोफ़ोन"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ब्लूटूथ माइक्रोफ़ोन"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"चालू है"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 5db8507a6c72..fc474dd2957e 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -287,7 +287,7 @@ <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Neka kôd za pokretanje sustava bude otključan"</string> <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Želite li dopustiti OEM otključavanje?"</string> <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"UPOZORENJE: značajke za zaštitu uređaja neće funkcionirati na ovom uređaju dok je ta postavka uključena."</string> - <string name="mock_location_app" msgid="6269380172542248304">"Odabir aplikacije za lažnu lokaciju"</string> + <string name="mock_location_app" msgid="6269380172542248304">"Odaberi aplikaciju za lažnu lokaciju"</string> <string name="mock_location_app_not_set" msgid="6972032787262831155">"Aplikacija za lažnu lokaciju nije postavljena"</string> <string name="mock_location_app_set" msgid="4706722469342913843">"Aplikacija za lažnu lokaciju: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="debug_networking_category" msgid="6829757985772659599">"Umrežavanje"</string> @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Uređaj se mora ponovno pokrenuti da bi se ta promjena primijenila. Ponovo pokrenite uređaj odmah ili odustanite."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalice"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB zvučnik"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Utičnica za mikrofon"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključeno"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index bd700108c92e..95b28928ab01 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Az eszközt újra kell indítani, hogy a módosítás megtörténjen. Indítsa újra most, vagy vesse el a módosítást."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Vezetékes fejhallgató"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fejhallgató"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-hangeszköz"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon jack csatlakozója"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Be"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 6549de0d1464..51190f3a2599 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Միացված է"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Սարքն անհրաժեշտ է վերագործարկել, որպեսզի փոփոխությունը կիրառվի։ Վերագործարկեք հիմա կամ չեղարկեք փոփոխությունը։"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Լարով ականջակալ"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Ականջակալ"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB աուդիո"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Խոսափողի հարակցիչ"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB խոսափող"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth խոսափող"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Միացնել"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 2555e9b3b918..84280cf8a0de 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -287,7 +287,7 @@ <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Izinkan bootloader dibuka kuncinya"</string> <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Izinkan buka kunci OEM?"</string> <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"PERINGATAN: Fitur perlindungan perangkat tidak akan berfungsi di perangkat ini saat setelan diaktifkan."</string> - <string name="mock_location_app" msgid="6269380172542248304">"Pilih aplikasi lokasi palsu"</string> + <string name="mock_location_app" msgid="6269380172542248304">"Pilih aplikasi lokasi simulasi"</string> <string name="mock_location_app_not_set" msgid="6972032787262831155">"Tidak ada aplikasi lokasi simulasi yang disetel"</string> <string name="mock_location_app_set" msgid="4706722469342913843">"Aplikasi lokasi palsu: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="debug_networking_category" msgid="6829757985772659599">"Jaringan"</string> @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Perangkat Anda harus di-reboot agar perubahan ini diterapkan. Reboot sekarang atau batalkan."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Headphone berkabel"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Colokan mikrofon"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktif"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index eba432f30088..daadae6010dd 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Endurræsa þarf tækið til að þessi breyting taki gildi. Endurræstu núna eða hættu við."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Heyrnartól með snúru"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Heyrnartól"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-hljóð"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Hljóðnematengi"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-hljóðnemi"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-hljóðnemi"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Kveikt"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index c0f1557a3aff..36aa2b334cbe 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -686,12 +686,13 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Per applicare questa modifica, devi riavviare il dispositivo. Riavvia ora o annulla."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Cuffie con cavo"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Cuffie"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Jack per microfono"</string> - <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfono USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> <skip /> + <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfono USB"</string> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfono BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambio della rete dell\'operatore"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index b6f863e16b1d..942b99b68897 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"צריך להפעיל מחדש את המכשיר כדי להחיל את השינוי. יש להפעיל מחדש עכשיו או לבטל."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"אוזניות חוטיות"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"אוזניות"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"אודיו ב-USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"שקע למיקרופון"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"מיקרופון ב-USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"מיקרופון BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"פועלת"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index ff3870ac5343..5924ab2a42ca 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"この変更を適用するには、デバイスの再起動が必要です。今すぐ再起動するか、キャンセルしてください。"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有線ヘッドフォン"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ヘッドフォン"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB オーディオ"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"マイク差込口"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB マイク"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT マイク"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ON"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 0ec064832255..5f3d54f2dc70 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ჩართული"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ამ ცვლილების ასამოქმედებლად თქვენი მოწყობილობა უნდა გადაიტვირთოს. გადატვირთეთ ახლავე ან გააუქმეთ."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"სადენიანი ყურსასმენი"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ყურსასმენი"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB აუდიო"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"მიკროფონის ჯეკი"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB მიკროფონი"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT მიკროფონი"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ჩართვა"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index f7547170b846..bb6e353ae321 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Қосулы"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бұл өзгеріс күшіне енуі үшін, құрылғыны қайта жүктеу керек. Қазір қайта жүктеңіз не бас тартыңыз."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Сымды құлақаспап"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Құлақаспап"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофон ұяшығы"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофоны"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Қосу"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index cac7c55685c4..20ed723f4bc2 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ត្រូវតែចាប់ផ្ដើមឧបករណ៍របស់អ្នកឡើងវិញ ដើម្បីឱ្យការផ្លាស់ប្ដូរនេះមានប្រសិទ្ធភាព។ ចាប់ផ្ដើមឡើងវិញឥឡូវនេះ ឬបោះបង់។"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"កាសមានខ្សែ"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"កាស"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ឧបករណ៍បំពងសំឡេង USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ឌុយមីក្រូហ្វូន"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"មីក្រូហ្វូន USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"មីក្រូហ្វូន BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"បើក"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 92534ae56b6f..50a1234ca348 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ಈ ಬದಲಾವಣೆ ಅನ್ವಯವಾಗಲು ನಿಮ್ಮ ಸಾಧನವನ್ನು ರೀಬೂಟ್ ಮಾಡಬೇಕು. ಇದೀಗ ರೀಬೂಟ್ ಮಾಡಿ ಅಥವಾ ರದ್ದುಗೊಳಿಸಿ."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ವೈಯರ್ ಹೊಂದಿರುವ ಹೆಡ್ಫೋನ್"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ಹೆಡ್ಫೋನ್"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ಆಡಿಯೋ"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ಮೈಕ್ ಜ್ಯಾಕ್"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ಮೈಕ್ರೊಫೋನ್"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ಮೈಕ್ರೊಫೋನ್"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ಆನ್ ಆಗಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 1bfe19ce2e2e..f9949ce97a4c 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"사용 설정됨"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"변경사항을 적용하려면 기기를 재부팅해야 합니다. 지금 재부팅하거나 취소하세요."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"유선 헤드폰"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"헤드폰"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 오디오"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"마이크 잭"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 마이크"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"블루투스 마이크"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"사용"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 1884b0326ed2..40bbb35bdec3 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бул өзгөрүү күчүнө кириши үчүн, түзмөктү өчүрүп күйгүзүңүз. Азыр же кийинчерээк өчүрүп күйгүзсөңүз болот."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Зымдуу гарнитура"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Гарнитура"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофондун оюкчасы"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофону"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Күйгүзүү"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 0f83134aeefa..b5f89225eec0 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ທ່ານຕ້ອງປິດເປີດອຸປະກອນຄືນໃໝ່ເພື່ອນຳໃຊ້ການປ່ຽນແປງນີ້. ປິດເປີດໃໝ່ດຽວນີ້ ຫຼື ຍົກເລີກ."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ຫູຟັງແບບມີສາຍ"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ຫູຟັງ"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ສຽງ USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ຊ່ອງສຽງໄມ"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ໄມໂຄຣໂຟນ USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ໄມໂຄຣໂຟນ BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ເປີດ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 3a81c917eaa6..fd23ecefecfa 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kad pakeitimas būtų pritaikytas, įrenginį reikia paleisti iš naujo. Dabar paleiskite iš naujo arba atšaukite."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Laidinės ausinės"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Ausinės"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB garsas"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofono jungtis"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofonas"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"„Bluetooth“ mikrofonas"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Įjungta"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 8e1f20e32ded..4fbbbe9ec028 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Lai šīs izmaiņas tiktu piemērotas, nepieciešama ierīces atkārtota palaišana. Atkārtoti palaidiet to tūlīt vai atceliet izmaiņas."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Vadu austiņas"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Austiņas"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofona ligzda"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofons"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofons"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ieslēgts"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 66de28295292..ea53b93dab15 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Овозможено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да се примени променава, уредот мора да се рестартира. Рестартирајте сега или откажете."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Жичени слушалки"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Слушалка"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудио"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Приклучок за микрофон"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Микрофон со Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Вклучено"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index fe2a996c151f..43ac8622a159 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"പ്രവർത്തനക്ഷമമാക്കി"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ഈ മാറ്റം ബാധകമാകുന്നതിന് നിങ്ങളുടെ ഉപകരണം റീബൂട്ട് ചെയ്യേണ്ടതുണ്ട്. ഇപ്പോൾ റീബൂട്ട് ചെയ്യുകയോ റദ്ദാക്കുകയോ ചെയ്യുക."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"വയേർഡ് ഹെഡ്ഫോൺ"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ഹെഡ്ഫോൺ"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ഓഡിയോ"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"മൈക്ക് ജാക്ക്"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB മൈക്രോഫോൺ"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT മൈക്രോഫോൺ"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ഓണാണ്"</string> diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml index 2ebbb1318c54..84c707f0eee8 100644 --- a/packages/SettingsLib/res/values-mn/arrays.xml +++ b/packages/SettingsLib/res/values-mn/arrays.xml @@ -225,7 +225,7 @@ <string-array name="animator_duration_scale_entries"> <item msgid="6416998593844817378">"Дүрс амилуулалт идэвхгүй"</item> <item msgid="875345630014338616">"Дүрс амилуулах далайц .5x"</item> - <item msgid="2753729231187104962">"Дүрс амилуулах далайц 1x"</item> + <item msgid="2753729231187104962">"Анимацийн масштаб 1x"</item> <item msgid="1368370459723665338">"Дүрс амилуулах далайц 1.5x"</item> <item msgid="5768005350534383389">"Дүрс амилуулалтын далайц 2x"</item> <item msgid="3728265127284005444">"Дүрс амилуулалтын далайц 5x"</item> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index c9668f169f0d..d3b67c752e7c 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Энэ өөрчлөлтийг хэрэгжүүлэхийн тулд таны төхөөрөмжийг дахин асаах ёстой. Одоо дахин асаах эсвэл цуцлана уу."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Утастай чихэвч"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Чихэвч"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофоны чихэвчний оролт"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT микрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Асаах"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 600779d1a915..9d22c0060f9e 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सुरू केले आहे"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"हा बदल लागू करण्यासाठी तुमचे डिव्हाइस रीबूट करणे आवश्यक आहे. आता रीबूट करा किंवा रद्द करा."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"वायर्ड हेडफोन"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"हेडफोन"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ऑडिओ"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइक जॅक"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB मायक्रोफोन"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT मायक्रोफोन"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"सुरू करा"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index d4eb1baabeab..22ff9dd5d41a 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Didayakan"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Peranti anda mesti dibut semula supaya perubahan ini berlaku. But semula sekarang atau batalkan."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fon kepala berwayar"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fon kepala"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Bicu mikrofon"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Hidup"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 92f9cbbb4c2b..9a337128786d 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ဖွင့်ထားသည်"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ဤအပြောင်းအလဲ ထည့်သွင်းရန် သင့်စက်ကို ပြန်လည်စတင်ရမည်။ ယခု ပြန်လည်စတင်ပါ သို့မဟုတ် ပယ်ဖျက်ပါ။"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ကြိုးတပ်နားကြပ်"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"နားကြပ်"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB အသံ"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"မိုက်ဂျက်ပင်"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB မိုက်ခရိုဖုန်း"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT မိုက်ခရိုဖုန်း"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ဖွင့်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 83f40afbc9b3..3b03d739673a 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten din må startes på nytt for at denne endringen skal tre i kraft. Start på nytt nå eller avbryt."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Hodetelefoner med kabel"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Hodetelefoner"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-lyd"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonkontakt"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"På"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index d9d12db7369f..84c8466abfaa 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सक्षम पारिएको छ"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"यो परिवर्तन लागू गर्न तपाईंको यन्त्र अनिवार्य रूपमा रिबुट गर्नु पर्छ। अहिले रिबुट गर्नुहोस् वा रद्द गर्नुहोस्।"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"तारयुक्त हेडफोन"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"हेडफोन"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB अडियो"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइकको ज्याक"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB माइक्रोफोन"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT माइक्रोफोन"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"अन छ"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index f5b54093e659..83df29acd836 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aan"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Je apparaat moet opnieuw worden opgestart om deze wijziging toe te passen. Start nu opnieuw op of annuleer de wijziging."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Bedrade koptelefoon"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Koptelefoon"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Microfoonaansluiting"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-microfoon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-microfoon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aan"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 508d2fcd3564..d03c8f30502e 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ଏହି ପରିବର୍ତ୍ତନ ଲାଗୁ କରିବା ପାଇଁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ନିଶ୍ଚିତ ରୂପେ ରିବୁଟ୍ କରାଯିବା ଆବଶ୍ୟକ। ବର୍ତ୍ତମାନ ରିବୁଟ୍ କରନ୍ତୁ କିମ୍ବା ବାତିଲ କରନ୍ତୁ।"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ତାରଯୁକ୍ତ ହେଡଫୋନ"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ହେଡଫୋନ"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ଅଡିଓ"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ମାଇକ ଜେକ"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ମାଇକ୍ରୋଫୋନ"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ମାଇକ୍ରୋଫୋନ"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ଚାଲୁ ଅଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index ebff08d9ec79..1d482076ca59 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ਇਸ ਤਬਦੀਲੀ ਨੂੰ ਲਾਗੂ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨੂੰ ਰੀਬੂਟ ਕਰਨਾ ਲਾਜ਼ਮੀ ਹੈ। ਹੁਣੇ ਰੀਬੂਟ ਕਰੋ ਜਾਂ ਰੱਦ ਕਰੋ।"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ਤਾਰ ਵਾਲੇ ਹੈੱਡਫ਼ੋਨ"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ਹੈੱਡਫ਼ੋਨ"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ਆਡੀਓ"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ਮਾਈਕ ਜੈਕ"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ਚਾਲੂ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index e5cd6f0bbdb6..e8492d33e39f 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -686,9 +686,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Włączono"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Wprowadzenie zmiany wymaga ponownego uruchomienia urządzenia. Uruchom ponownie teraz lub anuluj."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Słuchawki przewodowe"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Słuchawki"</string> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Przewodowe urządzenie audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Dźwięk przez USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Gniazdo mikrofonu"</string> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Mikrofon przewodowy"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Włączono"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml index 96f0c1a5b2ce..bb18c4719c72 100644 --- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml @@ -206,19 +206,19 @@ </string-array> <string-array name="window_animation_scale_entries"> <item msgid="2675263395797191850">"Animação desligada"</item> - <item msgid="5790132543372767872">"Escala da animação 0,5 x"</item> - <item msgid="2529692189302148746">"Escala da animação 1x"</item> + <item msgid="5790132543372767872">"Escala de animação 0,5 x"</item> + <item msgid="2529692189302148746">"Escala de animação 1x"</item> <item msgid="8072785072237082286">"Escala de animação 1,5 x"</item> - <item msgid="3531560925718232560">"Escala da animação 2x"</item> + <item msgid="3531560925718232560">"Escala de animação 2x"</item> <item msgid="4542853094898215187">"Escala de animação 5 x"</item> <item msgid="5643881346223901195">"Escala de animação 10 x"</item> </string-array> <string-array name="transition_animation_scale_entries"> <item msgid="3376676813923486384">"Animação desligada"</item> - <item msgid="753422683600269114">"Escala da animação 0,5 x"</item> - <item msgid="3695427132155563489">"Escala da animação 1x"</item> + <item msgid="753422683600269114">"Escala de animação 0,5 x"</item> + <item msgid="3695427132155563489">"Escala de animação 1x"</item> <item msgid="9032615844198098981">"Escala de animação 1,5 x"</item> - <item msgid="8473868962499332073">"Escala da animação 2x"</item> + <item msgid="8473868962499332073">"Escala de animação 2x"</item> <item msgid="4403482320438668316">"Escala de animação 5 x"</item> <item msgid="169579387974966641">"Escala de animação 10 x"</item> </string-array> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 7bc0db9fef8d..20bd69a2e15e 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -417,8 +417,8 @@ <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"É desativado depois de 1 dia"</string> <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"Ativado indefinidamente"</string> <string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animação da janela"</string> - <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string> - <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração do Animator"</string> + <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala anim. de transição"</string> + <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala duração Animator"</string> <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular telas secundárias"</string> <string name="debug_applications_category" msgid="5394089406638954196">"Apps"</string> <string name="immediately_destroy_activities" msgid="1826287490705167403">"Não manter atividades"</string> @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fones de ouvido com fio"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fone de ouvido"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ativado"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 94a21c8bc36d..488dd5aa5652 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reiniciar o dispositivo para aplicar esta alteração. Reinicie agora ou cancele."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auscultadores com fios"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auscultadores"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ligado"</string> diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml index 96f0c1a5b2ce..bb18c4719c72 100644 --- a/packages/SettingsLib/res/values-pt/arrays.xml +++ b/packages/SettingsLib/res/values-pt/arrays.xml @@ -206,19 +206,19 @@ </string-array> <string-array name="window_animation_scale_entries"> <item msgid="2675263395797191850">"Animação desligada"</item> - <item msgid="5790132543372767872">"Escala da animação 0,5 x"</item> - <item msgid="2529692189302148746">"Escala da animação 1x"</item> + <item msgid="5790132543372767872">"Escala de animação 0,5 x"</item> + <item msgid="2529692189302148746">"Escala de animação 1x"</item> <item msgid="8072785072237082286">"Escala de animação 1,5 x"</item> - <item msgid="3531560925718232560">"Escala da animação 2x"</item> + <item msgid="3531560925718232560">"Escala de animação 2x"</item> <item msgid="4542853094898215187">"Escala de animação 5 x"</item> <item msgid="5643881346223901195">"Escala de animação 10 x"</item> </string-array> <string-array name="transition_animation_scale_entries"> <item msgid="3376676813923486384">"Animação desligada"</item> - <item msgid="753422683600269114">"Escala da animação 0,5 x"</item> - <item msgid="3695427132155563489">"Escala da animação 1x"</item> + <item msgid="753422683600269114">"Escala de animação 0,5 x"</item> + <item msgid="3695427132155563489">"Escala de animação 1x"</item> <item msgid="9032615844198098981">"Escala de animação 1,5 x"</item> - <item msgid="8473868962499332073">"Escala da animação 2x"</item> + <item msgid="8473868962499332073">"Escala de animação 2x"</item> <item msgid="4403482320438668316">"Escala de animação 5 x"</item> <item msgid="169579387974966641">"Escala de animação 10 x"</item> </string-array> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 7bc0db9fef8d..20bd69a2e15e 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -417,8 +417,8 @@ <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"É desativado depois de 1 dia"</string> <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"Ativado indefinidamente"</string> <string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animação da janela"</string> - <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string> - <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração do Animator"</string> + <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala anim. de transição"</string> + <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala duração Animator"</string> <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular telas secundárias"</string> <string name="debug_applications_category" msgid="5394089406638954196">"Apps"</string> <string name="immediately_destroy_activities" msgid="1826287490705167403">"Não manter atividades"</string> @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fones de ouvido com fio"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fone de ouvido"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ativado"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 47ae7efe2652..edd03560c176 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pentru ca modificarea să se aplice, trebuie să repornești dispozitivul. Repornește-l acum sau anulează."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Căști cu fir"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Căști"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mufă pentru microfon"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfon BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activat"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 1342321d6dbf..55fbfa5711ba 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Включено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Чтобы изменение вступило в силу, необходимо перезапустить устройство. Вы можете сделать это сейчас или позже."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Проводные наушники"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Наушники"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудиоустройство"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофонный разъем"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-микрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Вкл."</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index be0356ad2cb9..d48824a744b4 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"සබලයි"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"මෙම වෙනස යෙදීමට ඔබේ උපාංගය නැවත පණ ගැන්විය යුතුය. දැන් නැවත පණ ගන්වන්න හෝ අවලංගු කරන්න."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"රැහැන්ගත හෙඩ්ෆෝන්"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"හෙඩ්ෆෝන්"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ශ්රව්ය"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"මයික් ජැක්කුව"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB මයික්රෆෝනය"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT මයික්රෆෝනය"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ක්රියාත්මකයි"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index e3a8a8cc2507..b80748296ad0 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Zmena sa prejaví až po reštarte zariadenia. Môžete ho teraz reštartovať alebo akciu zrušiť."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Slúchadlá s káblom"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slúchadlá"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvuk cez USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Konektor mikrofónu"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofón s rozhraním USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofón Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Zapnúť"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 717e7baf3634..a4c0622f368c 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Napravo je treba znova zagnati, da bo ta sprememba uveljavljena. Znova zaženite zdaj ali prekličite."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žične slušalke"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalke"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvok USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Vtič za mikrofon"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Vklop"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 97e6c7d62a59..19c42119d26e 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pajisja jote duhet të riniset që ky ndryshim të zbatohet. Rinise tani ose anuloje."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kufje me tela"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kufje"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Pajisja audio me USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Fisha e mikrofonit"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofoni me USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofoni me Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktive"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 3cf73a75c703..1862910ff94a 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Омогућено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Морате да рестартујете уређај да би се ова промена применила. Рестартујте га одмах или откажите."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Жичане слушалице"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Слушалице"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Утикач за микрофон"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Укључено"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 9586a0c86b67..1ee80c5fb822 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten måste startas om för att ändringen ska börja gälla. Starta om nu eller avbryt."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Hörlur med kabel"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Hörlur"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-ljud"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonuttag"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"På"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 8f0b246b1a8c..d33eafe24b69 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Ni lazima uwashe tena kifaa chako ili mabadiliko haya yatekelezwe. Washa tena sasa au ughairi."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kipokea sauti cha kichwani chenye waya"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kipokea sauti cha kichwani"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Sauti ya USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Pini ya maikrofoni"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Maikrofoni ya USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Maikrofoni ya Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Umewashwa"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index e1a697cdf1bd..2c0781d6837c 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"இயக்கப்பட்டது"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"இந்த மாற்றங்கள் செயல்படுத்தப்பட உங்கள் சாதனத்தை மறுபடி தொடங்க வேண்டும். இப்போதே மறுபடி தொடங்கவும் அல்லது ரத்துசெய்யவும்."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"வயர்டு ஹெட்ஃபோன்"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ஹெட்ஃபோன்"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ஆடியோ"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"மைக் ஜாக்"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB மைக்ரோஃபோன்"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT மைக்ரோஃபோன்"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ஆன்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 22e539ba7da0..145af84985b5 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ఎనేబుల్ చేయబడింది"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ఈ మార్పును వర్తింపజేయాలంటే మీరు మీ పరికరాన్ని తప్పనిసరిగా రీబూట్ చేయాలి. ఇప్పుడే రీబూట్ చేయండి లేదా రద్దు చేయండి."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"వైర్ ఉన్న హెడ్ఫోన్"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"హెడ్ఫోన్"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ఆడియో"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"మైక్ జాక్"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB మైక్రోఫోన్"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT మైక్రోఫోన్"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ఆన్లో ఉంది"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index c9d9309a3ba6..ae6585c51081 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"เปิดใช้"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"คุณต้องรีบูตอุปกรณ์เพื่อให้การเปลี่ยนแปลงนี้มีผล รีบูตเลยหรือยกเลิก"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"หูฟังแบบใช้สาย"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"หูฟัง"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"เสียง USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ช่องเสียบไมค์"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ไมโครโฟน USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ไมโครโฟน BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"เปิด"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 5d7ed8720a00..6e60e2b65932 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Na-enable"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Dapat i-reboot ang iyong device para mailapat ang pagbabagong ito. Mag-reboot ngayon o kanselahin."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired na headphone"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Jack ng mikropono"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB na mikropono"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT na mikropono"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Naka-on"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 9e4b6f2d22f8..8d709b9f3916 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu değişikliğin geçerli olması için cihazınızın yeniden başlatılması gerekir. Şimdi yeniden başlatın veya iptal edin."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kablolu kulaklık"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kulaklık"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ses cihazı"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon jakı"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofonu"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Açık"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 75c6b54dde1d..7b1086ca303f 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Щоб застосувати ці зміни, потрібний перезапуск. Перезапустіть пристрій або скасуйте зміни."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Дротові навушники"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Навушники"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудіо"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Гніздо для мікрофона"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-мікрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-мікрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Увімкнено"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index ebd2845d4799..db00474f0750 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -94,7 +94,7 @@ <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"منسلک ہے (فون کے علاوہ)، بیٹری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string> <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"منسلک ہے (میڈیا کے علاوہ)، بیٹری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string> <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"منسلک ہے (فون یا میڈیا کے علاوہ)، بیٹری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string> - <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"فعال۔ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string> + <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"فعال۔ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string> <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"فعال۔ L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> بیٹری۔"</string> <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"فعال ہے۔ بایاں: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string> <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"فعال ہے۔ دایاں: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string> @@ -112,7 +112,7 @@ <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"فعال ہے (بایاں اور دایاں)"</string> <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"فعال (صرف میڈیا)۔ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string> <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"فعال (صرف میڈیا)۔ L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> بیٹری۔"</string> - <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)۔ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string> + <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)۔ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string> <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)۔ L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> بیٹری۔"</string> <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)۔ بائيں: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string> <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)۔ دائيں: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string> @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"اس تبدیلی کو لاگو کرنے کے ليے آپ کے آلہ کو ریبوٹ کرنا ضروری ہے۔ ابھی ریبوٹ کریں یا منسوخ کریں۔"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"تار والا ہیڈ فون"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ہیڈ فون"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB آڈیو"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"مائیک جیک"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB مائیکروفون"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT مائیکروفون"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"آن"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 60fdf932f95f..833863e2eee7 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Oʻzgarishlar kuchga kirishi uchun qurilmani oʻchirib yoqing. Buni hozir yoki keyinroq bajarishingiz mumkin."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Simli quloqlik"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Quloqlik"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon ulagichi"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Yoniq"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index dbd270fb2e57..fba3a13864b5 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bạn phải khởi động lại thiết bị để áp dụng sự thay đổi này. Hãy khởi động lại ngay hoặc hủy."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Tai nghe có dây"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Tai nghe"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Âm thanh qua cổng USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Giắc cắm micrô"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrô USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrô BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Đang bật"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 5c50e7faf398..29a4590878cf 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已启用"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"设备必须重新启动才能应用此更改。您可以立即重新启动或取消。"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有线耳机"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"头戴式耳机"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音频"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麦克风插孔"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麦克风"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"蓝牙麦克风"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"开启"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 877fa7b10d9b..71fd6eec762b 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"你的裝置必須重新開機,才能套用此變更。請立即重新開機或取消。"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有線耳機"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"耳機"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音訊"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麥克風插孔"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麥克風"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"藍牙麥克風"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"開啟"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index fa016ba028e7..b5700bcb31ea 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"裝置必須重新啟動才能套用這項變更。請立即重新啟動或取消變更。"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有線耳機"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"耳機"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音訊"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麥克風插孔"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麥克風"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"藍牙麥克風"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"開啟"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 063b6e7937ea..2f822b35a704 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -686,9 +686,11 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kufanele idivayisi yakho iqaliswe ukuze lolu shintsho lusebenze. Qalisa manje noma khansela."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Amahedifoni anentambo"</string> - <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Amahedifoni"</string> + <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> + <skip /> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Umsindo we-USB"</string> - <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Umgodi we-earphone ye-mic"</string> + <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> + <skip /> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Imakrofoni ye-USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Imakrofoni ye-BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Vuliwe"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index abc58ee99904..fd2a1cb14edd 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -819,6 +819,11 @@ <!-- Summary of checkbox setting that enables the terminal app. [CHAR LIMIT=64] --> <string name="enable_terminal_summary">Enable terminal app that offers local shell access</string> + <!-- Title of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=32] --> + <string name="enable_linux_terminal_title">Linux development environment</string> + <!-- Summary of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=64] --> + <string name="enable_linux_terminal_summary">Run Linux terminal on Android</string> + <!-- HDCP checking title, used for debug purposes only. [CHAR LIMIT=25] --> <string name="hdcp_checking_title">HDCP checking</string> <!-- HDCP checking dialog title, used for debug purposes only. [CHAR LIMIT=25] --> diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java index d4d2b48fcc04..d91c6bd8e639 100644 --- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java @@ -30,11 +30,12 @@ import android.view.DisplayInfo; import android.view.IWindowManager; import android.view.WindowManagerGlobal; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.settingslib.R; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; import java.util.function.Predicate; /** @@ -82,38 +83,55 @@ public class DisplayDensityUtils { private final DisplayManager mDisplayManager; /** - * The text description of the density values of the default display. - */ - private String[] mDefaultDisplayDensityEntries; - - /** - * The density values of the default display. + * The text description of the density values. */ - private int[] mDefaultDisplayDensityValues; + @Nullable + private final String[] mEntries; /** - * The density values, indexed by display unique ID. + * The density values. */ - private final Map<String, int[]> mValuesPerDisplay = new HashMap(); + @Nullable + private final int[] mValues; - private int mDefaultDensityForDefaultDisplay; - private int mCurrentIndex = -1; + private final int mDefaultDensity; + private final int mCurrentIndex; - public DisplayDensityUtils(Context context) { + public DisplayDensityUtils(@NonNull Context context) { this(context, INTERNAL_ONLY); } /** - * Creates an instance that stores the density values for the displays that satisfy - * the predicate. + * Creates an instance that stores the density values for the smallest display that satisfies + * the predicate. It is enough to store the values for one display because the same density + * should be set to all the displays that satisfy the predicate. * @param context The context * @param predicate Determines what displays the density should be set for. The default display * must satisfy this predicate. */ - public DisplayDensityUtils(Context context, Predicate predicate) { + public DisplayDensityUtils(@NonNull Context context, + @NonNull Predicate<DisplayInfo> predicate) { mPredicate = predicate; mDisplayManager = context.getSystemService(DisplayManager.class); + Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); + DisplayInfo defaultDisplayInfo = new DisplayInfo(); + if (!defaultDisplay.getDisplayInfo(defaultDisplayInfo)) { + Log.w(LOG_TAG, "Cannot fetch display info for the default display"); + mEntries = null; + mValues = null; + mDefaultDensity = 0; + mCurrentIndex = -1; + return; + } + if (!mPredicate.test(defaultDisplayInfo)) { + throw new IllegalArgumentException( + "Predicate must not filter out the default display."); + } + + int idOfSmallestDisplay = Display.DEFAULT_DISPLAY; + int minDimensionPx = Math.min(defaultDisplayInfo.logicalWidth, + defaultDisplayInfo.logicalHeight); for (Display display : mDisplayManager.getDisplays( DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) { DisplayInfo info = new DisplayInfo(); @@ -122,121 +140,123 @@ public class DisplayDensityUtils { continue; } if (!mPredicate.test(info)) { - if (display.getDisplayId() == Display.DEFAULT_DISPLAY) { - throw new IllegalArgumentException("Predicate must not filter out the default " - + "display."); - } continue; } - - final int defaultDensity = DisplayDensityUtils.getDefaultDensityForDisplay( - display.getDisplayId()); - if (defaultDensity <= 0) { - Log.w(LOG_TAG, "Cannot fetch default density for display " - + display.getDisplayId()); - continue; + int minDimension = Math.min(info.logicalWidth, info.logicalHeight); + if (minDimension < minDimensionPx) { + minDimensionPx = minDimension; + idOfSmallestDisplay = display.getDisplayId(); } + } - final Resources res = context.getResources(); - - final int currentDensity = info.logicalDensityDpi; - int currentDensityIndex = -1; - - // Compute number of "larger" and "smaller" scales for this display. - final int minDimensionPx = Math.min(info.logicalWidth, info.logicalHeight); - final int maxDensity = - DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP; - final float maxScaleDimen = context.getResources().getFraction( - R.fraction.display_density_max_scale, 1, 1); - final float maxScale = Math.min(maxScaleDimen, maxDensity / (float) defaultDensity); - final float minScale = context.getResources().getFraction( - R.fraction.display_density_min_scale, 1, 1); - final float minScaleInterval = context.getResources().getFraction( - R.fraction.display_density_min_scale_interval, 1, 1); - final int numLarger = (int) MathUtils.constrain((maxScale - 1) / minScaleInterval, - 0, SUMMARIES_LARGER.length); - final int numSmaller = (int) MathUtils.constrain((1 - minScale) / minScaleInterval, - 0, SUMMARIES_SMALLER.length); - - String[] entries = new String[1 + numSmaller + numLarger]; - int[] values = new int[entries.length]; - int curIndex = 0; - - if (numSmaller > 0) { - final float interval = (1 - minScale) / numSmaller; - for (int i = numSmaller - 1; i >= 0; i--) { - // Round down to a multiple of 2 by truncating the low bit. - final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1; - if (currentDensity == density) { - currentDensityIndex = curIndex; - } - entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]); - values[curIndex] = density; - curIndex++; + final int defaultDensity = + DisplayDensityUtils.getDefaultDensityForDisplay(idOfSmallestDisplay); + if (defaultDensity <= 0) { + Log.w(LOG_TAG, "Cannot fetch default density for display " + idOfSmallestDisplay); + mEntries = null; + mValues = null; + mDefaultDensity = 0; + mCurrentIndex = -1; + return; + } + + final Resources res = context.getResources(); + + final int currentDensity = defaultDisplayInfo.logicalDensityDpi; + int currentDensityIndex = -1; + + // Compute number of "larger" and "smaller" scales for this display. + final int maxDensity = + DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP; + final float maxScaleDimen = context.getResources().getFraction( + R.fraction.display_density_max_scale, 1, 1); + final float maxScale = Math.min(maxScaleDimen, maxDensity / (float) defaultDensity); + final float minScale = context.getResources().getFraction( + R.fraction.display_density_min_scale, 1, 1); + final float minScaleInterval = context.getResources().getFraction( + R.fraction.display_density_min_scale_interval, 1, 1); + final int numLarger = (int) MathUtils.constrain((maxScale - 1) / minScaleInterval, + 0, SUMMARIES_LARGER.length); + final int numSmaller = (int) MathUtils.constrain((1 - minScale) / minScaleInterval, + 0, SUMMARIES_SMALLER.length); + + String[] entries = new String[1 + numSmaller + numLarger]; + int[] values = new int[entries.length]; + int curIndex = 0; + + if (numSmaller > 0) { + final float interval = (1 - minScale) / numSmaller; + for (int i = numSmaller - 1; i >= 0; i--) { + // Round down to a multiple of 2 by truncating the low bit. + final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1; + if (currentDensity == density) { + currentDensityIndex = curIndex; } + entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]); + values[curIndex] = density; + curIndex++; } + } - if (currentDensity == defaultDensity) { - currentDensityIndex = curIndex; - } - values[curIndex] = defaultDensity; - entries[curIndex] = res.getString(SUMMARY_DEFAULT); - curIndex++; - - if (numLarger > 0) { - final float interval = (maxScale - 1) / numLarger; - for (int i = 0; i < numLarger; i++) { - // Round down to a multiple of 2 by truncating the low bit. - final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1; - if (currentDensity == density) { - currentDensityIndex = curIndex; - } - values[curIndex] = density; - entries[curIndex] = res.getString(SUMMARIES_LARGER[i]); - curIndex++; + if (currentDensity == defaultDensity) { + currentDensityIndex = curIndex; + } + values[curIndex] = defaultDensity; + entries[curIndex] = res.getString(SUMMARY_DEFAULT); + curIndex++; + + if (numLarger > 0) { + final float interval = (maxScale - 1) / numLarger; + for (int i = 0; i < numLarger; i++) { + // Round down to a multiple of 2 by truncating the low bit. + final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1; + if (currentDensity == density) { + currentDensityIndex = curIndex; } + values[curIndex] = density; + entries[curIndex] = res.getString(SUMMARIES_LARGER[i]); + curIndex++; } + } - final int displayIndex; - if (currentDensityIndex >= 0) { - displayIndex = currentDensityIndex; - } else { - // We don't understand the current density. Must have been set by - // someone else. Make room for another entry... - int newLength = values.length + 1; - values = Arrays.copyOf(values, newLength); - values[curIndex] = currentDensity; + final int displayIndex; + if (currentDensityIndex >= 0) { + displayIndex = currentDensityIndex; + } else { + // We don't understand the current density. Must have been set by + // someone else. Make room for another entry... + int newLength = values.length + 1; + values = Arrays.copyOf(values, newLength); + values[curIndex] = currentDensity; - entries = Arrays.copyOf(entries, newLength); - entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity); + entries = Arrays.copyOf(entries, newLength); + entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity); - displayIndex = curIndex; - } - - if (display.getDisplayId() == Display.DEFAULT_DISPLAY) { - mDefaultDensityForDefaultDisplay = defaultDensity; - mCurrentIndex = displayIndex; - mDefaultDisplayDensityEntries = entries; - mDefaultDisplayDensityValues = values; - } - mValuesPerDisplay.put(info.uniqueId, values); + displayIndex = curIndex; } + + mDefaultDensity = defaultDensity; + mCurrentIndex = displayIndex; + mEntries = entries; + mValues = values; } - public String[] getDefaultDisplayDensityEntries() { - return mDefaultDisplayDensityEntries; + @Nullable + public String[] getEntries() { + return mEntries; } - public int[] getDefaultDisplayDensityValues() { - return mDefaultDisplayDensityValues; + @Nullable + public int[] getValues() { + return mValues; } - public int getCurrentIndexForDefaultDisplay() { + public int getCurrentIndex() { return mCurrentIndex; } - public int getDefaultDensityForDefaultDisplay() { - return mDefaultDensityForDefaultDisplay; + public int getDefaultDensity() { + return mDefaultDensity; } /** @@ -311,15 +331,9 @@ public class DisplayDensityUtils { if (!mPredicate.test(info)) { continue; } - if (!mValuesPerDisplay.containsKey(info.uniqueId)) { - Log.w(LOG_TAG, "Unable to save forced display density setting " - + "for display " + info.uniqueId); - continue; - } final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); - wm.setForcedDisplayDensityForUser(displayId, - mValuesPerDisplay.get(info.uniqueId)[index], userId); + wm.setForcedDisplayDensityForUser(displayId, mValues[index], userId); } } catch (RemoteException exc) { Log.w(LOG_TAG, "Unable to save forced display density setting"); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java index 6335e712f904..83ee9751329f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java @@ -125,7 +125,9 @@ public class InputMediaDevice extends MediaDevice { ? mProductName : mContext.getString(R.string.media_transfer_usb_device_mic_name); case TYPE_BLUETOOTH_SCO -> - mContext.getString(R.string.media_transfer_bt_device_mic_name); + mProductName != null + ? mProductName + : mContext.getString(R.string.media_transfer_bt_device_mic_name); default -> mContext.getString(R.string.media_transfer_this_device_name_desktop); }; } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java index 52c2a87cc961..9f9aaf5ff83a 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java @@ -16,13 +16,22 @@ package com.android.settingslib.devicestate; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; +import android.hardware.devicestate.DeviceState; +import android.hardware.devicestate.DeviceStateManager; import android.os.UserHandle; import android.provider.Settings; @@ -42,7 +51,10 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; @SmallTest @RunWith(AndroidJUnit4.class) @@ -53,6 +65,8 @@ public class DeviceStateRotationLockSettingsManagerTest { @Mock private Context mMockContext; @Mock private Resources mMockResources; + @Mock private DeviceStateManager mDeviceStateManager; + private DeviceStateRotationLockSettingsManager mManager; private int mNumSettingsChanges = 0; private final ContentObserver mContentObserver = new ContentObserver(null) { @@ -70,6 +84,9 @@ public class DeviceStateRotationLockSettingsManagerTest { when(mMockContext.getApplicationContext()).thenReturn(mMockContext); when(mMockContext.getResources()).thenReturn(mMockResources); when(mMockContext.getContentResolver()).thenReturn(context.getContentResolver()); + when(mMockContext.getSystemService(DeviceStateManager.class)).thenReturn( + mDeviceStateManager); + when(mDeviceStateManager.getSupportedDeviceStates()).thenReturn(createDeviceStateList()); when(mMockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)) .thenReturn(new String[]{"0:1", "1:0:2", "2:2"}); when(mMockResources.getIntArray(R.array.config_foldedDeviceStates)) @@ -180,4 +197,29 @@ public class DeviceStateRotationLockSettingsManagerTest { value, UserHandle.USER_CURRENT); } + + private List<DeviceState> createDeviceStateList() { + List<DeviceState> deviceStates = new ArrayList<>(); + deviceStates.add(createDeviceState(0 /* identifier */, "folded", + new HashSet<>(List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)), + new HashSet<>(List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)))); + deviceStates.add(createDeviceState(1 /* identifier */, "half_folded", + new HashSet<>(List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)), + new HashSet<>( + List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN)))); + deviceStates.add(createDeviceState(2, "unfolded", + new HashSet<>(List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)), + new HashSet<>(List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)))); + + return deviceStates; + } + + private DeviceState createDeviceState(int identifier, @NonNull String name, + @NonNull Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties, + @NonNull Set<@DeviceState.PhysicalDeviceStateProperties Integer> physicalProperties) { + DeviceState.Configuration deviceStateConfiguration = new DeviceState.Configuration.Builder( + identifier, name).setPhysicalProperties(systemProperties).setPhysicalProperties( + physicalProperties).build(); + return new DeviceState(deviceStateConfiguration); + } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt index d91c2fa66ca8..7a905cba491d 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt @@ -18,6 +18,16 @@ package com.android.settingslib.devicestate import android.content.Context import android.content.res.Resources +import android.hardware.devicestate.DeviceState +import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN +import android.hardware.devicestate.DeviceStateManager +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY @@ -32,14 +42,40 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags +import org.mockito.Mockito.`when` as whenever private const val DEVICE_STATE_UNKNOWN = 0 -private const val DEVICE_STATE_CLOSED = 1 -private const val DEVICE_STATE_HALF_FOLDED = 2 -private const val DEVICE_STATE_OPEN = 3 -private const val DEVICE_STATE_REAR_DISPLAY = 4 +private val DEVICE_STATE_CLOSED = DeviceState( + DeviceState.Configuration.Builder(/* identifier= */ 1, "CLOSED") + .setSystemProperties(setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)) + .setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)) + .build() +) +private val DEVICE_STATE_HALF_FOLDED = DeviceState( + DeviceState.Configuration.Builder(/* identifier= */ 2, "HALF_FOLDED") + .setSystemProperties(setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) + .setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN)) + .build() +) +private val DEVICE_STATE_OPEN = DeviceState( + DeviceState.Configuration.Builder(/* identifier= */ 3, "OPEN") + .setSystemProperties(setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) + .setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)) + .build() +) +private val DEVICE_STATE_REAR_DISPLAY = DeviceState( + DeviceState.Configuration.Builder(/* identifier= */ 4, "REAR_DISPLAY") + .setSystemProperties( + setOf( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY, + PROPERTY_FEATURE_REAR_DISPLAY + ) + ) + .setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)) + .build() +) @SmallTest @RunWith(AndroidJUnit4::class) @@ -51,6 +87,8 @@ class PosturesHelperTest { @Mock private lateinit var resources: Resources + @Mock private lateinit var deviceStateManager: DeviceStateManager + private lateinit var posturesHelper: PosturesHelper @Before @@ -59,30 +97,39 @@ class PosturesHelperTest { whenever(context.resources).thenReturn(resources) whenever(resources.getIntArray(R.array.config_foldedDeviceStates)) - .thenReturn(intArrayOf(DEVICE_STATE_CLOSED)) + .thenReturn(intArrayOf(DEVICE_STATE_CLOSED.identifier)) whenever(resources.getIntArray(R.array.config_halfFoldedDeviceStates)) - .thenReturn(intArrayOf(DEVICE_STATE_HALF_FOLDED)) + .thenReturn(intArrayOf(DEVICE_STATE_HALF_FOLDED.identifier)) whenever(resources.getIntArray(R.array.config_openDeviceStates)) - .thenReturn(intArrayOf(DEVICE_STATE_OPEN)) + .thenReturn(intArrayOf(DEVICE_STATE_OPEN.identifier)) whenever(resources.getIntArray(R.array.config_rearDisplayDeviceStates)) - .thenReturn(intArrayOf(DEVICE_STATE_REAR_DISPLAY)) + .thenReturn(intArrayOf(DEVICE_STATE_REAR_DISPLAY.identifier)) + whenever(deviceStateManager.supportedDeviceStates).thenReturn( + listOf( + DEVICE_STATE_CLOSED, + DEVICE_STATE_HALF_FOLDED, + DEVICE_STATE_OPEN, + DEVICE_STATE_REAR_DISPLAY + ) + ) - posturesHelper = PosturesHelper(context) + posturesHelper = PosturesHelper(context, deviceStateManager) } @Test - fun deviceStateToPosture_mapsCorrectly() { + @RequiresFlagsDisabled(DeviceStateManagerFlags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION) + fun deviceStateToPosture_mapsCorrectly_overlayConfigurationValues() { expect - .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_CLOSED)) + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_CLOSED.identifier)) .isEqualTo(DEVICE_STATE_ROTATION_KEY_FOLDED) expect - .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED)) + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED.identifier)) .isEqualTo(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED) expect - .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_OPEN)) + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_OPEN.identifier)) .isEqualTo(DEVICE_STATE_ROTATION_KEY_UNFOLDED) expect - .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY)) + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY.identifier)) .isEqualTo(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY) expect .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_UNKNOWN)) @@ -90,19 +137,58 @@ class PosturesHelperTest { } @Test - fun postureToDeviceState_mapsCorrectly() { + @RequiresFlagsEnabled(DeviceStateManagerFlags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION) + fun deviceStateToPosture_mapsCorrectly_deviceStateManager() { + expect + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_CLOSED.identifier)) + .isEqualTo(DEVICE_STATE_ROTATION_KEY_FOLDED) + expect + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED.identifier)) + .isEqualTo(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED) + expect + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_OPEN.identifier)) + .isEqualTo(DEVICE_STATE_ROTATION_KEY_UNFOLDED) + expect + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY.identifier)) + .isEqualTo(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY) + expect + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_UNKNOWN)) + .isEqualTo(DEVICE_STATE_ROTATION_KEY_UNKNOWN) + } + + @Test + @RequiresFlagsDisabled(DeviceStateManagerFlags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION) + fun postureToDeviceState_mapsCorrectly_overlayConfigurationValues() { + expect + .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED)) + .isEqualTo(DEVICE_STATE_CLOSED.identifier) + expect + .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)) + .isEqualTo(DEVICE_STATE_HALF_FOLDED.identifier) + expect + .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED)) + .isEqualTo(DEVICE_STATE_OPEN.identifier) + expect + .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)) + .isEqualTo(DEVICE_STATE_REAR_DISPLAY.identifier) + expect.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNKNOWN)).isNull() + } + + @Test + @RequiresFlagsEnabled(DeviceStateManagerFlags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION) + fun postureToDeviceState_mapsCorrectly_deviceStateManager() { expect .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED)) - .isEqualTo(DEVICE_STATE_CLOSED) + .isEqualTo(DEVICE_STATE_CLOSED.identifier) expect .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)) - .isEqualTo(DEVICE_STATE_HALF_FOLDED) + .isEqualTo(DEVICE_STATE_HALF_FOLDED.identifier) expect .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED)) - .isEqualTo(DEVICE_STATE_OPEN) + .isEqualTo(DEVICE_STATE_OPEN.identifier) expect .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)) - .isEqualTo(DEVICE_STATE_REAR_DISPLAY) + .isEqualTo(DEVICE_STATE_REAR_DISPLAY.identifier) expect.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNKNOWN)).isNull() } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java index 6c1cb7015225..7775b912e51d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java @@ -44,6 +44,7 @@ public class InputMediaDeviceTest { private static final String PRODUCT_NAME_BUILTIN_MIC = "Built-in Mic"; private static final String PRODUCT_NAME_WIRED_HEADSET = "My Wired Headset"; private static final String PRODUCT_NAME_USB_HEADSET = "My USB Headset"; + private static final String PRODUCT_NAME_BT_HEADSET = "My Bluetooth Headset"; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -142,6 +143,21 @@ public class InputMediaDeviceTest { MAX_VOLUME, CURRENT_VOLUME, IS_VOLUME_FIXED, + PRODUCT_NAME_BT_HEADSET); + assertThat(btMediaDevice).isNotNull(); + assertThat(btMediaDevice.getName()).isEqualTo(PRODUCT_NAME_BT_HEADSET); + } + + @Test + public void getName_returnCorrectName_btHeadset_nullProductName() { + InputMediaDevice btMediaDevice = + InputMediaDevice.create( + mContext, + String.valueOf(BT_HEADSET_ID), + AudioDeviceInfo.TYPE_BLUETOOTH_SCO, + MAX_VOLUME, + CURRENT_VOLUME, + IS_VOLUME_FIXED, null); assertThat(btMediaDevice).isNotNull(); assertThat(btMediaDevice.getName()) diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 4dc84246afc0..d3ee40083c91 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -282,5 +282,6 @@ public class SecureSettings { Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, Settings.Secure.MANDATORY_BIOMETRICS, Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, + Settings.Secure.ADVANCED_PROTECTION_MODE, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 688676dc4072..d34ccc5a2173 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -442,5 +442,6 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.MANDATORY_BIOMETRICS, new InclusiveIntegerRangeValidator(0, 1)); VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, new InclusiveIntegerRangeValidator(0, 1)); + VALIDATORS.put(Secure.ADVANCED_PROTECTION_MODE, BOOLEAN_VALIDATOR); } } diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp index 253145468e47..3350efc33ad8 100644 --- a/packages/Shell/Android.bp +++ b/packages/Shell/Android.bp @@ -8,7 +8,10 @@ package { } // used both for the android_app and android_library -shell_srcs = ["src/**/*.java",":dumpstate_aidl"] +shell_srcs = [ + "src/**/*.java", + ":dumpstate_aidl", +] shell_static_libs = ["androidx.legacy_legacy-support-v4"] android_app { @@ -22,6 +25,9 @@ android_app { libs: [ "device_policy_aconfig_flags_lib", ], + flags_packages: [ + "android.security.flags-aconfig", + ], platform_apis: true, certificate: "platform", privileged: true, @@ -43,4 +49,7 @@ android_library { static_libs: shell_static_libs, platform_apis: true, manifest: "AndroidManifest.xml", + flags_packages: [ + "android.security.flags-aconfig", + ], } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 408ed1e861c3..05c5e5d9f82d 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -924,6 +924,7 @@ <!-- Permission required for CTS test - CtsPackageManagerTestCases--> <uses-permission android:name="android.permission.DOMAIN_VERIFICATION_AGENT" /> + <uses-permission android:name="android.permission.VERIFICATION_AGENT" /> <!-- Permission required for Cts test - CtsInputTestCases --> <uses-permission @@ -941,6 +942,11 @@ <!-- Permission required for CTS test - CtsNfcTestCases --> <uses-permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" /> + <!-- Permission required for CTS test - AdvancedProtectionManagerTest --> + <uses-permission android:name="android.permission.SET_ADVANCED_PROTECTION_MODE" + android:featureFlag="android.security.aapm_api"/> + <uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" + android:featureFlag="android.security.aapm_api"/> <!-- Permission required for CTS test - CtsAppTestCases --> <uses-permission android:name="android.permission.KILL_UID" /> diff --git a/packages/StatementService/Parser/Android.bp b/packages/StatementService/Parser/Android.bp new file mode 100644 index 000000000000..c8af1344f4c7 --- /dev/null +++ b/packages/StatementService/Parser/Android.bp @@ -0,0 +1,26 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_library { + name: "StatementServiceParser", + use_resource_processor: true, + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + target_sdk_version: "29", +} diff --git a/core/java/android/service/wallpaper/AndroidManifest.xml b/packages/StatementService/Parser/AndroidManifest.xml index f1bdb76409fb..a3a99ac0552d 100644 --- a/core/java/android/service/wallpaper/AndroidManifest.xml +++ b/packages/StatementService/Parser/AndroidManifest.xml @@ -1,12 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project +<!-- Copyright (C) 2024 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + 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, @@ -16,7 +15,5 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="android.service.wallpaper"> - - <uses-sdk android:minSdkVersion="29" /> -</manifest> + package="com.android.statementservice.parser"> +</manifest>
\ No newline at end of file diff --git a/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt new file mode 100644 index 000000000000..4314f86fad37 --- /dev/null +++ b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:JvmName("DalComponentParser") + +package com.android.statementservice.parser + +import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB +import android.os.PatternMatcher.PATTERN_LITERAL +import android.os.PatternMatcher.PATTERN_PREFIX +import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB + +/** + * Parses a DAL component matching expression to Android's {@link android.os.PatternMatcher} type + * and pattern. Matching expressions support the following wildcards: + * + * 1) An asterisk (*) matches zero to as many characters as possible + * 2) A question mark (?) matches any single character. + * + * Matching one to many characters can be done with a question mark followed by an asterisk (?+). + * + * @param expression A matching expression string from a DAL relation extension component used for + * matching a URI part. This must be a non-empty string and all characters in the + * string should be decoded. + * + * @return Returns a Pair containing a {@link android.os.PatternMatcher} type and pattern. + */ +fun parseMatchingExpression(expression: String): Pair<Int, String> { + if (expression.isNullOrEmpty()) { + throw IllegalArgumentException("Matching expressions cannot be an empty string") + } + var count = 0 + var isAdvanced = expression.contains("?*") + val pattern = buildString { + for (char in expression) { + when (char) { + '*' -> { + if (this.endsWith('.') && !this.endsWith("\\.")) { + append('+') + } else { + count += 1 + append(".*") + } + } + '?' -> { + count += 1 + append('.') + } + '.' -> { + append("\\.") + } + '[', ']', '{', '}' -> { + if (isAdvanced) { + append('\\') + } + append(char) + } + else -> append(char) + } + } + } + if (count == 0) { + return Pair(PATTERN_LITERAL, pattern) + } + if (count == 1 && pattern.endsWith(".*")) { + return Pair(PATTERN_PREFIX, pattern.dropLast(2)) + } + if (isAdvanced) { + return Pair(PATTERN_ADVANCED_GLOB, pattern) + } + return Pair(PATTERN_SIMPLE_GLOB, pattern) +}
\ No newline at end of file diff --git a/packages/StatementService/TEST_MAPPING b/packages/StatementService/TEST_MAPPING new file mode 100644 index 000000000000..0714c9366665 --- /dev/null +++ b/packages/StatementService/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit" : [ + { + "name": "StatementServiceTests" + } + ] +}
\ No newline at end of file diff --git a/packages/StatementService/tests/Android.bp b/packages/StatementService/tests/Android.bp new file mode 100644 index 000000000000..ec1bd96a0bc0 --- /dev/null +++ b/packages/StatementService/tests/Android.bp @@ -0,0 +1,30 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package { + default_applicable_licenses: ["frameworks_base_license"], + default_team: "trendy_team_framework_android_packages", +} + +android_test { + name: "StatementServiceTests", + use_resource_processor: true, + test_suites: ["general-tests"], + srcs: ["src/**/*.kt"], + static_libs: [ + "androidx.test.ext.junit", + "androidx.test.runner", + "StatementServiceParser", + "truth", + ], +} diff --git a/packages/StatementService/tests/AndroidManifest.xml b/packages/StatementService/tests/AndroidManifest.xml new file mode 100644 index 000000000000..bde0f953252c --- /dev/null +++ b/packages/StatementService/tests/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.statementservice.test"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.statementservice.test" /> +</manifest> diff --git a/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt new file mode 100644 index 000000000000..44a56ec91458 --- /dev/null +++ b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.statementservice.parser + +import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB +import android.os.PatternMatcher.PATTERN_LITERAL +import android.os.PatternMatcher.PATTERN_PREFIX +import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class DalComponentParserTest { + + @Test + fun parseExpressions() { + validateParsedExpression("foobar", PATTERN_LITERAL, "foobar") + validateParsedExpression("foo.bar", PATTERN_LITERAL, "foo\\.bar") + validateParsedExpression("foo*", PATTERN_PREFIX, "foo") + validateParsedExpression("*bar", PATTERN_SIMPLE_GLOB, ".*bar") + validateParsedExpression("foo*bar", PATTERN_SIMPLE_GLOB, "foo.*bar") + validateParsedExpression("foo.*bar", PATTERN_SIMPLE_GLOB, "foo\\..*bar") + validateParsedExpression("*foo*bar", PATTERN_SIMPLE_GLOB, ".*foo.*bar") + validateParsedExpression("foo?bar", PATTERN_SIMPLE_GLOB, "foo.bar") + validateParsedExpression("foo.?bar", PATTERN_SIMPLE_GLOB, "foo\\..bar") + validateParsedExpression("?bar", PATTERN_SIMPLE_GLOB, ".bar") + validateParsedExpression("foo?", PATTERN_SIMPLE_GLOB, "foo.") + validateParsedExpression("fo?b*r", PATTERN_SIMPLE_GLOB, "fo.b.*r") + validateParsedExpression("?*bar", PATTERN_ADVANCED_GLOB, ".+bar") + validateParsedExpression("foo?*bar", PATTERN_ADVANCED_GLOB, "foo.+bar") + validateParsedExpression("foo?*bar*", PATTERN_ADVANCED_GLOB, "foo.+bar.*") + validateParsedExpression("foo*?bar", PATTERN_SIMPLE_GLOB, "foo.*.bar") + + // set matches are not supported in DAL + validateParsedExpression("foo[a-z]", PATTERN_LITERAL, "foo[a-z]") + validateParsedExpression("foo[a-z]+", PATTERN_LITERAL, "foo[a-z]+") + validateParsedExpression("foo[a-z]*", PATTERN_PREFIX, "foo[a-z]") + validateParsedExpression("[a-z]*bar", PATTERN_SIMPLE_GLOB, "[a-z].*bar") + validateParsedExpression("foo[a-z]?bar", PATTERN_SIMPLE_GLOB, "foo[a-z].bar") + validateParsedExpression("foo[a-z]?*bar", PATTERN_ADVANCED_GLOB, "foo\\[a-z\\].+bar") + + // range matches are not supported in DAL + validateParsedExpression("fo{2}", PATTERN_LITERAL, "fo{2}") + validateParsedExpression("fo{2}+", PATTERN_LITERAL, "fo{2}+") + validateParsedExpression("fo{2}*", PATTERN_PREFIX, "fo{2}") + validateParsedExpression("fo{2}*bar", PATTERN_SIMPLE_GLOB, "fo{2}.*bar") + validateParsedExpression("fo{2}?*", PATTERN_ADVANCED_GLOB, "fo\\{2\\}.+") + validateParsedExpression("foo{2}?*bar", PATTERN_ADVANCED_GLOB, "foo\\{2\\}.+bar") + } + + @Test(expected = IllegalArgumentException::class) + fun parseEmptyExpression() { + parseMatchingExpression("") + } + + private fun validateParsedExpression(given: String, expectedType: Int, expectedFilter: String) { + val (type, filter) = parseMatchingExpression(given) + assertThat(filter).isEqualTo(expectedFilter) + assertThat(type).isEqualTo(expectedType) + assertThat(filter).isEqualTo(expectedFilter) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 28635318236d..3c560fdcadb6 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -87,7 +87,6 @@ filegroup { srcs: [ "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt", "tests/src/**/systemui/broadcast/ActionReceiverTest.kt", - "tests/src/**/systemui/doze/DozeMachineTest.java", "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java", "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java", "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt", @@ -175,7 +174,6 @@ filegroup { "tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt", "tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt", "tests/src/**/systemui/controls/ui/DetailDialogTest.kt", - "tests/src/**/systemui/doze/DozeMachineTest.kt", "tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt", "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt", "tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java", @@ -318,9 +316,6 @@ filegroup { "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt", "tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt", "tests/src/**/keyguard/ClockEventControllerTest.kt", - "tests/src/**/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt", - "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.kt", - "tests/src/**/keyguard/LegacyLockIconViewControllerTest.java", "tests/src/**/systemui/animation/TransitionAnimatorTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt", @@ -417,7 +412,6 @@ filegroup { "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt", "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt", "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java", - "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.java", "tests/src/**/keyguard/CarrierTextManagerTest.java", "tests/src/**/systemui/ScreenDecorationsTest.java", "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt", @@ -899,9 +893,9 @@ android_robolectric_test { "androidx.compose.runtime_runtime", ], libs: [ - "android.test.runner.stubs.system", - "android.test.base.stubs.system", - "android.test.mock.stubs.system", + "android.test.runner.impl", + "android.test.base.impl", + "android.test.mock.impl", "truth", ], @@ -936,9 +930,9 @@ android_robolectric_test { "androidx.compose.runtime_runtime", ], libs: [ - "android.test.runner.stubs.system", - "android.test.base.stubs.system", - "android.test.mock.stubs.system", + "android.test.runner.impl", + "android.test.base.impl", + "android.test.mock.impl", "truth", ], @@ -974,9 +968,9 @@ android_ravenwood_test { "androidx.compose.runtime_runtime", ], libs: [ - "android.test.runner.stubs.system", - "android.test.base.stubs.system", - "android.test.mock.stubs.system", + "android.test.runner.impl", + "android.test.base.impl", + "android.test.mock.impl", ], auto_gen_config: true, plugins: [ diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index a21a80506279..c9235fcdeb2c 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -298,6 +298,22 @@ flag { } flag { + name: "status_bar_ui_thread" + namespace: "systemui" + description: "Move the StatusBar window to a new UI thread, which is separate from the main " + "thread." + bug: "374159193" +} + +flag { + name: "notification_shade_ui_thread" + namespace: "systemui" + description: "Move the NotificationShade window to a new UI thread, which is separate from " + "the main thread." + bug: "374159657" +} + +flag { name: "new_aod_transition" namespace: "systemui" description: "New LOCKSCREEN <=> AOD transition" @@ -395,9 +411,9 @@ flag { } flag { - name: "status_bar_ron_chips" + name: "status_bar_notification_chips" namespace: "systemui" - description: "Show rich ongoing notifications as chips in the status bar" + description: "Show promoted ongoing notifications as chips in the status bar" bug: "361346412" } @@ -1481,3 +1497,49 @@ flag { bug: "370863642" } +flag { + name: "notes_role_qs_tile" + namespace: "systemui" + description: "Enables notes role qs tile which opens default notes role app in app bubbles" + bug: "357863750" +} + +flag { + name: "ignore_touches_next_to_notification_shelf" + namespace: "systemui" + description: "The shelf can vertically overlap the unlock icon. Ignore touches if so." + bug: "358424256" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "media_projection_request_attribution_fix" + namespace: "systemui" + description: "Ensure MediaProjection consent requests are properly attributed" + bug: "373581993" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "secondary_user_widget_host" + namespace: "systemui" + description: "Host communal widgets in the current secondary user on HSUM." + bug: "373874416" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "only_show_media_stream_slider_in_single_volume_mode" + namespace: "systemui" + description: "When the device is in single volume mode, only show media stream slider and hide all other stream (e.g. call, notification, alarm, etc) sliders in volume panel" + bug: "373729625" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt index 9dc93484a638..4cf264253bf8 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt @@ -486,8 +486,8 @@ class TransitionAnimator( endState: State, windowBackgroundLayer: GradientDrawable, fadeWindowBackgroundLayer: Boolean = true, - useSpring: Boolean = false, drawHole: Boolean = false, + useSpring: Boolean = false, ): Animation { val transitionContainer = controller.transitionContainer val transitionContainerOverlay = transitionContainer.overlay diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt index 7dc2901273b2..c1c3b1fb6c5a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt @@ -28,6 +28,7 @@ import androidx.compose.animation.core.snap import androidx.compose.animation.core.tween import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement @@ -46,7 +47,6 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowDown -import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem @@ -64,6 +64,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.key.onKeyEvent @@ -72,6 +73,9 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.testTag +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpOffset @@ -88,7 +92,6 @@ import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.SceneTransitionLayout import com.android.compose.animation.scene.transitions -import com.android.compose.modifiers.thenIf import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel import com.android.systemui.bouncer.ui.BouncerDialogFactory @@ -131,7 +134,7 @@ fun BouncerContent( layout: BouncerSceneLayout, viewModel: BouncerSceneContentViewModel, dialogFactory: BouncerDialogFactory, - modifier: Modifier + modifier: Modifier, ) { Box( // Allows the content within each of the layouts to react to the appearance and @@ -140,31 +143,17 @@ fun BouncerContent( // Despite the keyboard only being part of the password bouncer, adding it at this level is // both necessary to properly handle the keyboard in all layouts and harmless in cases when // the keyboard isn't used (like the PIN or pattern auth methods). - modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent), + modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent) ) { when (layout) { - BouncerSceneLayout.STANDARD_BOUNCER -> - StandardLayout( - viewModel = viewModel, - ) + BouncerSceneLayout.STANDARD_BOUNCER -> StandardLayout(viewModel = viewModel) BouncerSceneLayout.BESIDE_USER_SWITCHER -> - BesideUserSwitcherLayout( - viewModel = viewModel, - ) - BouncerSceneLayout.BELOW_USER_SWITCHER -> - BelowUserSwitcherLayout( - viewModel = viewModel, - ) - BouncerSceneLayout.SPLIT_BOUNCER -> - SplitLayout( - viewModel = viewModel, - ) + BesideUserSwitcherLayout(viewModel = viewModel) + BouncerSceneLayout.BELOW_USER_SWITCHER -> BelowUserSwitcherLayout(viewModel = viewModel) + BouncerSceneLayout.SPLIT_BOUNCER -> SplitLayout(viewModel = viewModel) } - Dialog( - bouncerViewModel = viewModel, - dialogFactory = dialogFactory, - ) + Dialog(bouncerViewModel = viewModel, dialogFactory = dialogFactory) } } @@ -173,31 +162,19 @@ fun BouncerContent( * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.). */ @Composable -private fun StandardLayout( - viewModel: BouncerSceneContentViewModel, - modifier: Modifier = Modifier, -) { +private fun StandardLayout(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) { val isHeightExpanded = LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded FoldAware( - modifier = - modifier.padding( - start = 32.dp, - top = 92.dp, - end = 32.dp, - bottom = 48.dp, - ), + modifier = modifier.padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 48.dp), viewModel = viewModel, aboveFold = { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth(), ) { - StatusMessage( - viewModel = viewModel.message, - modifier = Modifier, - ) + StatusMessage(viewModel = viewModel.message, modifier = Modifier) OutputArea( viewModel = viewModel, @@ -210,9 +187,7 @@ private fun StandardLayout( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth(), ) { - Box( - modifier = Modifier.weight(1f), - ) { + Box(modifier = Modifier.weight(1f)) { InputArea( viewModel = viewModel, pinButtonRowVerticalSpacing = 12.dp, @@ -221,10 +196,7 @@ private fun StandardLayout( ) } - ActionArea( - viewModel = viewModel, - modifier = Modifier.padding(top = 48.dp), - ) + ActionArea(viewModel = viewModel, modifier = Modifier.padding(top = 48.dp)) } }, ) @@ -235,10 +207,7 @@ private fun StandardLayout( * by double-tapping on the side. */ @Composable -private fun SplitLayout( - viewModel: BouncerSceneContentViewModel, - modifier: Modifier = Modifier, -) { +private fun SplitLayout(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) { val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle() Row( @@ -248,12 +217,10 @@ private fun SplitLayout( .padding( horizontal = 24.dp, vertical = if (authMethod is PasswordBouncerViewModel) 24.dp else 48.dp, - ), + ) ) { // Left side (in left-to-right locales). - Box( - modifier = Modifier.fillMaxHeight().weight(1f), - ) { + Box(modifier = Modifier.fillMaxHeight().weight(1f)) { when (authMethod) { is PinBouncerViewModel -> { StatusMessage( @@ -263,7 +230,7 @@ private fun SplitLayout( OutputArea( viewModel = viewModel, modifier = - Modifier.align(Alignment.Center).sysuiResTag("bouncer_text_entry") + Modifier.align(Alignment.Center).sysuiResTag("bouncer_text_entry"), ) ActionArea( @@ -293,9 +260,7 @@ private fun SplitLayout( } // Right side (in right-to-left locales). - Box( - modifier = Modifier.fillMaxHeight().weight(1f), - ) { + Box(modifier = Modifier.fillMaxHeight().weight(1f)) { when (authMethod) { is PinBouncerViewModel, is PatternBouncerViewModel -> { @@ -311,13 +276,11 @@ private fun SplitLayout( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth().align(Alignment.Center), ) { - StatusMessage( - viewModel = viewModel.message, - ) + StatusMessage(viewModel = viewModel.message) OutputArea( viewModel = viewModel, modifier = - Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry") + Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry"), ) } } @@ -365,7 +328,7 @@ private fun BesideUserSwitcherLayout( .padding( top = if (isHeightExpanded) 128.dp else 96.dp, bottom = if (isHeightExpanded) 128.dp else 48.dp, - ), + ) ) { LaunchedEffect(isSwapped) { swapAnimationEnd = false } val animatedOffset by @@ -419,14 +382,12 @@ private fun BesideUserSwitcherLayout( aboveFold = { Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) { - StatusMessage( - viewModel = viewModel.message, - ) + StatusMessage(viewModel = viewModel.message) OutputArea( viewModel = viewModel, - modifier = Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry") + modifier = Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry"), ) } }, @@ -444,7 +405,7 @@ private fun BesideUserSwitcherLayout( Box( modifier = Modifier.weight(1f) - .padding(top = (if (addSpacingBetweenOutputAndInput) 24 else 0).dp), + .padding(top = (if (addSpacingBetweenOutputAndInput) 24 else 0).dp) ) { InputArea( viewModel = viewModel, @@ -470,16 +431,8 @@ private fun BelowUserSwitcherLayout( viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier, ) { - Column( - modifier = - modifier.padding( - vertical = 128.dp, - ) - ) { - UserSwitcher( - viewModel = viewModel, - modifier = Modifier.fillMaxWidth(), - ) + Column(modifier = modifier.padding(vertical = 128.dp)) { + UserSwitcher(viewModel = viewModel, modifier = Modifier.fillMaxWidth()) Spacer(Modifier.weight(1f)) @@ -488,9 +441,7 @@ private fun BelowUserSwitcherLayout( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth(), ) { - StatusMessage( - viewModel = viewModel.message, - ) + StatusMessage(viewModel = viewModel.message) OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp)) InputArea( @@ -500,10 +451,7 @@ private fun BelowUserSwitcherLayout( modifier = Modifier.padding(top = 128.dp), ) - ActionArea( - viewModel = viewModel, - modifier = Modifier.padding(top = 48.dp), - ) + ActionArea(viewModel = viewModel, modifier = Modifier.padding(top = 48.dp)) } } } @@ -533,19 +481,11 @@ private fun FoldAware( SceneTransitionLayout(state, modifier = modifier) { scene(SceneKeys.ContiguousSceneKey) { - FoldableScene( - aboveFold = aboveFold, - belowFold = belowFold, - isSplit = false, - ) + FoldableScene(aboveFold = aboveFold, belowFold = belowFold, isSplit = false) } scene(SceneKeys.SplitSceneKey) { - FoldableScene( - aboveFold = aboveFold, - belowFold = belowFold, - isSplit = true, - ) + FoldableScene(aboveFold = aboveFold, belowFold = belowFold, isSplit = true) } } } @@ -562,9 +502,7 @@ private fun SceneScope.FoldableScene( R.dimen.motion_layout_half_fold_bouncer_height_ratio ) - Column( - modifier = modifier.fillMaxHeight(), - ) { + Column(modifier = modifier.fillMaxHeight()) { // Content above the fold, when split on a foldable device in a "table top" posture: Box( modifier = @@ -575,7 +513,7 @@ private fun SceneScope.FoldableScene( } else { Modifier } - ), + ) ) { aboveFold() } @@ -590,7 +528,7 @@ private fun SceneScope.FoldableScene( } else { 1f } - ), + ) ) { belowFold() } @@ -598,10 +536,7 @@ private fun SceneScope.FoldableScene( } @Composable -private fun StatusMessage( - viewModel: BouncerMessageViewModel, - modifier: Modifier = Modifier, -) { +private fun StatusMessage(viewModel: BouncerMessageViewModel, modifier: Modifier = Modifier) { val message: MessageViewModel? by viewModel.message.collectAsStateWithLifecycle() DisposableEffect(Unit) { @@ -634,7 +569,7 @@ private fun StatusMessage( fontSize = 14.sp, lineHeight = 20.sp, overflow = TextOverflow.Ellipsis, - maxLines = 2 + maxLines = 2, ) } } @@ -647,22 +582,19 @@ private fun StatusMessage( * For example, this can be the PIN shapes or password text field. */ @Composable -private fun OutputArea( - viewModel: BouncerSceneContentViewModel, - modifier: Modifier = Modifier, -) { +private fun OutputArea(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) { val authMethodViewModel: AuthMethodBouncerViewModel? by viewModel.authMethodViewModel.collectAsStateWithLifecycle() when (val nonNullViewModel = authMethodViewModel) { is PinBouncerViewModel -> PinInputDisplay( viewModel = nonNullViewModel, - modifier = modifier.sysuiResTag("bouncer_text_entry") + modifier = modifier.sysuiResTag("bouncer_text_entry"), ) is PasswordBouncerViewModel -> PasswordBouncer( viewModel = nonNullViewModel, - modifier = modifier.sysuiResTag("bouncer_text_entry") + modifier = modifier.sysuiResTag("bouncer_text_entry"), ) else -> Unit } @@ -703,10 +635,7 @@ private fun InputArea( } @Composable -private fun ActionArea( - viewModel: BouncerSceneContentViewModel, - modifier: Modifier = Modifier, -) { +private fun ActionArea(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) { val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsStateWithLifecycle() val appearFadeInAnimatable = remember { Animatable(0f) } @@ -722,7 +651,7 @@ private fun ActionArea( durationMillis = 450, delayMillis = 133, easing = Easings.LegacyDecelerate, - ) + ), ) } LaunchedEffect(Unit) { @@ -733,39 +662,35 @@ private fun ActionArea( durationMillis = 450, delayMillis = 133, easing = Easings.StandardDecelerate, - ) + ), ) } Box( modifier = - modifier.graphicsLayer { - // Translate the button up from an initially pushed-down position: - translationY = (1 - appearMoveAnimatable.value) * appearAnimationInitialOffset - // Fade the button in: - alpha = appearFadeInAnimatable.value - }, + modifier + .graphicsLayer { + // Translate the button up from an initially pushed-down position: + translationY = + (1 - appearMoveAnimatable.value) * appearAnimationInitialOffset + // Fade the button in: + alpha = appearFadeInAnimatable.value + } + .height(56.dp) + .clip(ButtonDefaults.shape) + .background(color = MaterialTheme.colorScheme.tertiaryContainer) + .semantics { role = Role.Button } + .combinedClickable( + onClick = { actionButtonViewModel.onClick() }, + onLongClick = actionButtonViewModel.onLongClick?.let { { it.invoke() } }, + ) ) { - Button( - onClick = actionButtonViewModel.onClick, - modifier = - Modifier.height(56.dp).thenIf(actionButtonViewModel.onLongClick != null) { - Modifier.combinedClickable( - onClick = actionButtonViewModel.onClick, - onLongClick = actionButtonViewModel.onLongClick, - ) - }, - colors = - ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer, - ), - ) { - Text( - text = actionButtonViewModel.label, - style = MaterialTheme.typography.bodyMedium, - ) - } + Text( + text = actionButtonViewModel.label, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onTertiaryContainer, + modifier = Modifier.align(Alignment.Center).padding(ButtonDefaults.ContentPadding), + ) } } } @@ -800,15 +725,10 @@ private fun Dialog( /** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */ @Composable -private fun UserSwitcher( - viewModel: BouncerSceneContentViewModel, - modifier: Modifier = Modifier, -) { +private fun UserSwitcher(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) { if (!viewModel.isUserSwitcherVisible) { // Take up the same space as the user switcher normally would, but with nothing inside it. - Box( - modifier = modifier, - ) + Box(modifier = modifier) return } @@ -891,7 +811,7 @@ private fun UserSwitcherDropdownMenu( MaterialTheme( colorScheme = MaterialTheme.colorScheme.copy( - surface = MaterialTheme.colorScheme.surfaceContainerHighest, + surface = MaterialTheme.colorScheme.surfaceContainerHighest ), shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(28.dp)), ) { @@ -932,9 +852,7 @@ private fun UserSwitcherDropdownMenu( * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of * the two reaches a stopping point but `0` in the middle of the transition. */ -private fun animatedAlpha( - offset: Float, -): Float { +private fun animatedAlpha(offset: Float): Float { // Describes a curve that is made of two parabolic U-shaped curves mirrored horizontally around // the y-axis. The U on the left runs between x = -1 and x = 0 while the U on the right runs // between x = 0 and x = 1. diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 96c47cc5fb6f..4ab526188ffe 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -765,7 +765,7 @@ private fun BoxScope.CommunalHubLazyGrid( alpha = { outlineAlpha }, modifier = Modifier.requiredSize(dpSize).thenIf( - dragDropState.draggingItemIndex != index + dragDropState.draggingItemKey != item.key ) { Modifier.animateItem( placementSpec = spring(stiffness = Spring.StiffnessMediumLow) @@ -778,7 +778,7 @@ private fun BoxScope.CommunalHubLazyGrid( dragDropState = dragDropState, selected = selected, enabled = item.isWidgetContent(), - index = index, + key = item.key, ) { isDragging -> CommunalContent( modifier = Modifier.fillMaxSize(), diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index 101385f88825..0718bc331d41 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -38,8 +38,9 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntRect import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.round import androidx.compose.ui.unit.toOffset import androidx.compose.ui.unit.toSize import com.android.systemui.Flags.communalWidgetResizing @@ -93,7 +94,7 @@ internal constructor( private val scope: CoroutineScope, private val updateDragPositionForRemove: (offset: Offset) -> Boolean, ) { - var draggingItemIndex by mutableStateOf<Int?>(null) + var draggingItemKey by mutableStateOf<Any?>(null) private set var isDraggingToRemove by mutableStateOf(false) @@ -105,6 +106,8 @@ internal constructor( private var draggingItemInitialOffset by mutableStateOf(Offset.Zero) private var dragStartPointerOffset by mutableStateOf(Offset.Zero) + private var previousTargetItemKey: Any? = null + internal val draggingItemOffset: Offset get() = draggingItemLayoutInfo?.let { item -> @@ -112,7 +115,7 @@ internal constructor( } ?: Offset.Zero private val draggingItemLayoutInfo: LazyGridItemInfo? - get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex } + get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.key == draggingItemKey } /** * Called when dragging is initiated. @@ -137,7 +140,7 @@ internal constructor( .firstItemAtOffset(normalizedOffset - contentOffset) ?.apply { dragStartPointerOffset = normalizedOffset - this.offset.toOffset() - draggingItemIndex = index + draggingItemKey = key draggingItemInitialOffset = this.offset.toOffset() return true } @@ -146,16 +149,19 @@ internal constructor( } internal fun onDragInterrupted() { - draggingItemIndex?.let { + draggingItemKey?.let { if (isDraggingToRemove) { - contentListState.onRemove(it) + contentListState.onRemove( + contentListState.list.indexOfFirst { it.key == draggingItemKey } + ) isDraggingToRemove = false updateDragPositionForRemove(Offset.Zero) } // persist list editing changes on dragging ends contentListState.onSaveList() - draggingItemIndex = null + draggingItemKey = null } + previousTargetItemKey = null draggingItemDraggedDelta = Offset.Zero draggingItemInitialOffset = Offset.Zero dragStartPointerOffset = Offset.Zero @@ -170,15 +176,29 @@ internal constructor( val startOffset = draggingItem.offset.toOffset() + draggingItemOffset val endOffset = startOffset + draggingItem.size.toSize() val middleOffset = startOffset + (endOffset - startOffset) / 2f + val draggingBoundingBox = + IntRect(draggingItem.offset + draggingItemOffset.round(), draggingItem.size) val targetItem = - state.layoutInfo.visibleItemsInfo - .asSequence() - .filter { item -> contentListState.isItemEditable(item.index) } - .filter { item -> draggingItem.index != item.index } - .firstItemAtOffset(middleOffset) + if (communalWidgetResizing()) { + state.layoutInfo.visibleItemsInfo.findLast { item -> + val itemBoundingBox = IntRect(item.offset, item.size) + draggingItemKey != item.key && + contentListState.isItemEditable(item.index) && + draggingBoundingBox.contains(itemBoundingBox.center) + } + } else { + state.layoutInfo.visibleItemsInfo + .asSequence() + .filter { item -> contentListState.isItemEditable(item.index) } + .filter { item -> draggingItem.index != item.index } + .firstItemAtOffset(middleOffset) + } - if (targetItem != null) { + if ( + targetItem != null && + (!communalWidgetResizing() || targetItem.key != previousTargetItemKey) + ) { val scrollToIndex = if (targetItem.index == state.firstVisibleItemIndex) { draggingItem.index @@ -187,6 +207,14 @@ internal constructor( } else { null } + if (communalWidgetResizing()) { + // Keep track of the previous target item, to avoid rapidly oscillating between + // items if the target item doesn't visually move as a result of the index change. + // In this case, even after the index changes, we'd still be colliding with the + // element, so it would be selected as the target item the next time this function + // runs again, which would trigger us to revert the index change we recently made. + previousTargetItemKey = targetItem.key + } if (scrollToIndex != null) { scope.launch { // this is needed to neutralize automatic keeping the first item first. @@ -196,20 +224,17 @@ internal constructor( } else { contentListState.onMove(draggingItem.index, targetItem.index) } - draggingItemIndex = targetItem.index isDraggingToRemove = false - } else { + } else if (targetItem == null) { val overscroll = checkForOverscroll(startOffset, endOffset) if (overscroll != 0f) { scrollChannel.trySend(overscroll) } isDraggingToRemove = checkForRemove(startOffset) + previousTargetItemKey = null } } - private val LazyGridItemInfo.offsetEnd: IntOffset - get() = this.offset + this.size - /** Calculate the amount dragged out of bound on both sides. Returns 0f if not overscrolled */ private fun checkForOverscroll(startOffset: Offset, endOffset: Offset): Float { return when { @@ -237,7 +262,7 @@ fun Modifier.dragContainer( viewModel: BaseCommunalViewModel, ): Modifier { return this.then( - pointerInput(dragDropState, contentOffset) { + Modifier.pointerInput(dragDropState, contentOffset) { detectDragGesturesAfterLongPress( onDrag = { change, offset -> change.consume() @@ -273,7 +298,7 @@ fun Modifier.dragContainer( @Composable fun LazyGridItemScope.DraggableItem( dragDropState: GridDragDropState, - index: Int, + key: Any, enabled: Boolean, selected: Boolean, modifier: Modifier = Modifier, @@ -283,7 +308,7 @@ fun LazyGridItemScope.DraggableItem( return content(false) } - val dragging = index == dragDropState.draggingItemIndex + val dragging = key == dragDropState.draggingItemKey val itemAlpha: Float by animateFloatAsState( targetValue = if (dragDropState.isDraggingToRemove) 0.5f else 1f, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt index 9444664885c8..71230f9cde12 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt @@ -17,8 +17,9 @@ package com.android.systemui.keyguard.ui.composable.modifier import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer @@ -41,15 +42,17 @@ fun Modifier.burnInAware( params: BurnInParameters, isClock: Boolean = false, ): Modifier { - val translationYState = remember { mutableStateOf(0F) } - viewModel.updateBurnInParams(params.copy(translationY = { translationYState.value })) + val cachedYTranslation = remember { mutableFloatStateOf(0f) } + LaunchedEffect(Unit) { + viewModel.updateBurnInParams(params.copy(translationY = { cachedYTranslation.floatValue })) + } val burnIn = viewModel.movement val translationX by burnIn.map { it.translationX.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f) val translationY by burnIn.map { it.translationY.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f) - translationYState.value = translationY + cachedYTranslation.floatValue = translationY val scaleViewModel by burnIn .map { BurnInScaleViewModel(scale = it.scale, scaleClockOnly = it.scaleClockOnly) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt index 4c6834cf6bea..834a7f5220ab 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt @@ -50,9 +50,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue @@ -60,6 +62,7 @@ import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer @@ -95,13 +98,17 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.shade.ui.composable.ShadeHeader +import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding +import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_CORNER_RADIUS import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel +import kotlin.math.max import kotlin.math.roundToInt +import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch @@ -315,6 +322,12 @@ fun SceneScope.NotificationScrollingStack( */ val stackHeight = remember { mutableIntStateOf(0) } + /** + * Space available for the notification stack on the screen. These bounds don't scroll off the + * screen, and respect the scrim paddings, scrim clipping. + */ + val stackBoundsOnScreen = remember { mutableStateOf(Rect.Zero) } + val scrimRounding = viewModel.shadeScrimRounding.collectAsStateWithLifecycle(ShadeScrimRounding()) @@ -348,12 +361,19 @@ fun SceneScope.NotificationScrollingStack( // The top y bound of the IME. val imeTop = remember { mutableFloatStateOf(0f) } - // we are not scrolled to the top unless the scrim is at its maximum offset. - LaunchedEffect(viewModel, scrimOffset) { - snapshotFlow { scrimOffset.value >= 0f } - .collect { isScrolledToTop -> viewModel.setScrolledToTop(isScrolledToTop) } + val shadeScrollState by remember { + derivedStateOf { + ShadeScrollState( + // we are not scrolled to the top unless the scrim is at its maximum offset. + isScrolledToTop = scrimOffset.value >= 0f, + scrollPosition = scrollState.value, + maxScrollPosition = scrollState.maxValue, + ) + } } + LaunchedEffect(shadeScrollState) { viewModel.setScrollState(shadeScrollState) } + // if contentHeight drops below minimum visible scrim height while scrim is // expanded, reset scrim offset. LaunchedEffect(stackHeight, scrimOffset) { @@ -395,6 +415,38 @@ fun SceneScope.NotificationScrollingStack( } } + // TalkBack sends a scroll event, when it wants to navigate to an item that is not displayed in + // the current viewport. + LaunchedEffect(viewModel) { + viewModel.setAccessibilityScrollEventConsumer { event -> + // scroll up, or down by the height of the visible portion of the notification stack + val direction = + when (event) { + AccessibilityScrollEvent.SCROLL_UP -> -1 + AccessibilityScrollEvent.SCROLL_DOWN -> 1 + } + val viewPortHeight = stackBoundsOnScreen.value.height + val scrollStep = max(0f, viewPortHeight - stackScrollView.stackBottomInset) + val scrollPosition = scrollState.value.toFloat() + val scrollRange = scrollState.maxValue.toFloat() + val targetScroll = (scrollPosition + direction * scrollStep).coerceIn(0f, scrollRange) + coroutineScope.launch { + scrollNotificationStack( + delta = targetScroll - scrollPosition, + animate = false, + scrimOffset = scrimOffset, + minScrimOffset = minScrimOffset, + scrollState = scrollState, + ) + } + } + try { + awaitCancellation() + } finally { + viewModel.setAccessibilityScrollEventConsumer(null) + } + } + val scrimNestedScrollConnection = shadeSession.rememberSession( scrimOffset, @@ -520,6 +572,9 @@ fun SceneScope.NotificationScrollingStack( .verticalScroll(scrollState) .padding(top = topPadding) .fillMaxWidth() + .onGloballyPositioned { coordinates -> + stackBoundsOnScreen.value = coordinates.boundsInWindow() + } ) { NotificationPlaceholder( stackScrollView = stackScrollView, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt index 54497f621c73..2a91bd8b1d73 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt @@ -133,7 +133,14 @@ fun SceneScope.QuickSettingsLayout( Column( verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding), horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding), + modifier = + modifier + .fillMaxWidth() + .padding( + start = QuickSettingsShade.Dimensions.Padding, + end = QuickSettingsShade.Dimensions.Padding, + top = QuickSettingsShade.Dimensions.Padding, + ), ) { BrightnessSliderContainer( viewModel = viewModel.brightnessSliderViewModel, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt index fe97405fab15..e9b7335197b0 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt @@ -16,34 +16,58 @@ package com.android.systemui.statusbar.phone +import android.app.Dialog import android.content.Context import android.content.res.Configuration import android.os.Bundle import androidx.annotation.GravityInt +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.AnchoredDraggableDefaults +import androidx.compose.foundation.gestures.AnchoredDraggableState +import androidx.compose.foundation.gestures.DraggableAnchors +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.anchoredDraggable import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsDraggedAsState import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawing +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.isSpecified import com.android.compose.theme.PlatformTheme +import com.android.systemui.keyboard.shortcut.ui.composable.hasCompactWindowSize import com.android.systemui.res.R +import kotlin.math.roundToInt /** * Create a [SystemUIDialog] with the given [content]. @@ -97,6 +121,9 @@ fun SystemUIDialogFactory.createBottomSheet( theme: Int = R.style.Theme_SystemUI_BottomSheet, dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK, content: @Composable (SystemUIDialog) -> Unit, + isDraggable: Boolean = true, + // TODO(b/337205027): remove maxWidth parameter when aligned to M3 spec + maxWidth: Dp = Dp.Unspecified, ): ComponentSystemUIDialog { return create( context = context, @@ -104,9 +131,49 @@ fun SystemUIDialogFactory.createBottomSheet( dismissOnDeviceLock = dismissOnDeviceLock, delegate = EdgeToEdgeDialogDelegate(), content = { dialog -> + val dragState = + if (isDraggable) + remember { AnchoredDraggableState(initialValue = DragAnchors.Start) } + else null + val interactionSource = + if (isDraggable) remember { MutableInteractionSource() } else null + if (dragState != null) { + val isDragged by interactionSource!!.collectIsDraggedAsState() + LaunchedEffect(dragState.currentValue, isDragged) { + if (!isDragged && dragState.currentValue == DragAnchors.End) dialog.dismiss() + } + } Box( - modifier = Modifier.bottomSheetClickable { dialog.dismiss() }, - contentAlignment = Alignment.BottomCenter + modifier = + Modifier.bottomSheetClickable { dialog.dismiss() } + .then( + if (isDraggable) + Modifier.anchoredDraggable( + state = dragState!!, + interactionSource = interactionSource, + orientation = Orientation.Vertical, + flingBehavior = + AnchoredDraggableDefaults.flingBehavior( + state = dragState + ), + ) + .offset { + IntOffset(x = 0, y = dragState.requireOffset().roundToInt()) + } + .onSizeChanged { layoutSize -> + val dragEndPoint = layoutSize.height - dialog.height + dragState.updateAnchors( + DraggableAnchors { + DragAnchors.entries.forEach { anchor -> + anchor at dragEndPoint * anchor.fraction + } + } + ) + } + .padding(top = draggableTopPadding()) + else Modifier // No-Op + ), + contentAlignment = Alignment.BottomCenter, ) { val radius = dimensionResource(R.dimen.bottom_sheet_corner_radius) Surface( @@ -114,8 +181,11 @@ fun SystemUIDialogFactory.createBottomSheet( Modifier.bottomSheetPaddings() // consume input so it doesn't get to the parent Composable .bottomSheetClickable {} - // TODO(b/337205027) change width - .widthIn(max = 800.dp), + .widthIn( + max = + if (maxWidth.isSpecified) maxWidth + else DraggableBottomSheet.MaxWidth + ), shape = RoundedCornerShape(topStart = radius, topEnd = radius), color = MaterialTheme.colorScheme.surfaceContainer, ) { @@ -127,7 +197,17 @@ fun SystemUIDialogFactory.createBottomSheet( } ) ) { - content(dialog) + if (isDraggable) { + Column( + Modifier.wrapContentWidth(Alignment.CenterHorizontally), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + DragHandle(dialog) + content(dialog) + } + } else { + content(dialog) + } } } } @@ -135,6 +215,11 @@ fun SystemUIDialogFactory.createBottomSheet( ) } +private enum class DragAnchors(val fraction: Float) { + Start(0f), + End(1f), +} + private fun SystemUIDialogFactory.create( context: Context, theme: Int, @@ -177,7 +262,7 @@ private fun Modifier.bottomSheetPaddings(): Modifier { padding( start = insets.getLeft(this, LocalLayoutDirection.current).toDp() + horizontalPadding, top = insets.getTop(this).toDp(), - end = insets.getRight(this, LocalLayoutDirection.current).toDp() + horizontalPadding + end = insets.getRight(this, LocalLayoutDirection.current).toDp() + horizontalPadding, ) } } @@ -191,3 +276,32 @@ private fun Modifier.bottomSheetPaddings(): Modifier { @Composable private fun Modifier.bottomSheetClickable(onClick: () -> Unit) = pointerInput(onClick) { detectTapGestures { onClick() } } + +@Composable +private fun DragHandle(dialog: Dialog) { + // TODO(b/373340318): Rename drag handle string resource. + val dragHandleContentDescription = + stringResource(id = R.string.shortcut_helper_content_description_drag_handle) + Surface( + modifier = + Modifier.padding(top = 16.dp, bottom = 6.dp) + .semantics { contentDescription = dragHandleContentDescription } + .clickable { dialog.dismiss() }, + color = MaterialTheme.colorScheme.outlineVariant, + shape = MaterialTheme.shapes.extraLarge, + ) { + Box(Modifier.size(width = 32.dp, height = 4.dp)) + } +} + +@Composable +private fun draggableTopPadding(): Dp { + return if (hasCompactWindowSize()) DraggableBottomSheet.DefaultTopPadding + else DraggableBottomSheet.LargeScreenTopPadding +} + +private object DraggableBottomSheet { + val DefaultTopPadding = 64.dp + val LargeScreenTopPadding = 72.dp + val MaxWidth = 640.dp +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt index 1cc0fb2aad9b..cf74785b6d4a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.semantics.paneTitle import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.android.compose.theme.PlatformTheme import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.res.R import com.android.systemui.volume.panel.ui.layout.ComponentsLayout @@ -43,30 +42,20 @@ private const val VolumePanelTestTag = "VolumePanel" private val padding = 24.dp @Composable -fun VolumePanelRoot( - viewModel: VolumePanelViewModel, - modifier: Modifier = Modifier, -) { +fun VolumePanelRoot(viewModel: VolumePanelViewModel, modifier: Modifier = Modifier) { val accessibilityTitle = stringResource(R.string.accessibility_volume_settings) val state: VolumePanelState by viewModel.volumePanelState.collectAsStateWithLifecycle() val components by viewModel.componentsLayout.collectAsStateWithLifecycle() with(VolumePanelComposeScope(state)) { components?.let { componentsState -> - PlatformTheme { - Components( - componentsState, - modifier - .sysuiResTag(VolumePanelTestTag) - .semantics { paneTitle = accessibilityTitle } - .padding( - start = padding, - top = padding, - end = padding, - bottom = 20.dp, - ) - ) - } + Components( + componentsState, + modifier + .sysuiResTag(VolumePanelTestTag) + .semantics { paneTitle = accessibilityTitle } + .padding(start = padding, top = padding, end = padding, bottom = 20.dp), + ) } } } @@ -74,7 +63,7 @@ fun VolumePanelRoot( @Composable private fun VolumePanelComposeScope.Components( layout: ComponentsLayout, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { val arrangement: Arrangement.Vertical = if (isLargeScreen) { @@ -82,14 +71,11 @@ private fun VolumePanelComposeScope.Components( } else { if (isPortrait) Arrangement.spacedBy(padding) else Arrangement.spacedBy(4.dp) } - Column( - modifier = modifier, - verticalArrangement = arrangement, - ) { + Column(modifier = modifier, verticalArrangement = arrangement) { if (isPortrait || isLargeScreen) { VerticalVolumePanelContent( modifier = Modifier.weight(weight = 1f, fill = false), - layout = layout + layout = layout, ) } else { HorizontalVolumePanelContent( @@ -97,23 +83,17 @@ private fun VolumePanelComposeScope.Components( layout = layout, ) } - BottomBar( - modifier = Modifier, - layout = layout, - ) + BottomBar(modifier = Modifier, layout = layout) } } @Composable private fun VolumePanelComposeScope.BottomBar( layout: ComponentsLayout, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { if (layout.bottomBarComponent.isVisible) { - Box( - modifier = modifier.fillMaxWidth(), - contentAlignment = Alignment.Center, - ) { + Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { with(layout.bottomBarComponent.component as ComposeVolumePanelUiComponent) { Content(Modifier) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index 7e288ddd3a4c..9f99c372e2a8 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -631,7 +631,13 @@ internal class NestedScrollHandlerImpl( return@PriorityNestedScrollConnection false } - _lastPointersInfo = pointersInfoOwner.pointersInfo() + val pointersInfo = pointersInfoOwner.pointersInfo() + + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + _lastPointersInfo = pointersInfo // If the current swipe transition is *not* closed to 0f or 1f, then we want the // scroll events to intercept the current transition to continue the scene @@ -650,7 +656,12 @@ internal class NestedScrollHandlerImpl( val isZeroOffset = if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f - _lastPointersInfo = pointersInfoOwner.pointersInfo() + val pointersInfo = pointersInfoOwner.pointersInfo() + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + _lastPointersInfo = pointersInfo val canStart = when (behavior) { @@ -685,7 +696,12 @@ internal class NestedScrollHandlerImpl( // We could start an overscroll animation canChangeScene = false - _lastPointersInfo = pointersInfoOwner.pointersInfo() + val pointersInfo = pointersInfoOwner.pointersInfo() + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + _lastPointersInfo = pointersInfo val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable) if (canStart) { @@ -707,6 +723,12 @@ internal class NestedScrollHandlerImpl( onScroll = { offsetAvailable, _ -> val controller = dragController ?: error("Should be called after onStart") + val pointersInfo = pointersInfoOwner.pointersInfo() + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection 0f + } + // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is // initiated in a nested child. controller.onDrag(delta = offsetAvailable) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt index ca68c256fd73..772872719ebe 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt @@ -22,19 +22,52 @@ interface ElementMatcher { fun matches(key: ElementKey, content: ContentKey): Boolean } +/** Returns an [ElementMatcher] that matches any element in [content]. */ +fun inContent(content: ContentKey): ElementMatcher { + val matcherContent = content + return object : ElementMatcher { + override fun matches(key: ElementKey, content: ContentKey): Boolean { + return content == matcherContent + } + } +} + +/** Returns an [ElementMatcher] that matches all elements not matching [this] matcher. */ +operator fun ElementMatcher.not(): ElementMatcher { + val delegate = this + return object : ElementMatcher { + override fun matches(key: ElementKey, content: ContentKey): Boolean { + return !delegate.matches(key, content) + } + } +} + +/** + * Returns an [ElementMatcher] that matches all elements matching both [this] matcher and [other]. + */ +infix fun ElementMatcher.and(other: ElementMatcher): ElementMatcher { + val delegate = this + return object : ElementMatcher { + override fun matches(key: ElementKey, content: ContentKey): Boolean { + return delegate.matches(key, content) && other.matches(key, content) + } + } +} + /** - * Returns an [ElementMatcher] that matches elements in [content] also matching [this] - * [ElementMatcher]. + * Returns an [ElementMatcher] that matches all elements either [this] matcher, or [other], or both. */ -fun ElementMatcher.inContent(content: ContentKey): ElementMatcher { +infix fun ElementMatcher.or(other: ElementMatcher): ElementMatcher { val delegate = this - val matcherScene = content return object : ElementMatcher { override fun matches(key: ElementKey, content: ContentKey): Boolean { - return content == matcherScene && delegate.matches(key, content) + return delegate.matches(key, content) || other.matches(key, content) } } } -@Deprecated("Use inContent() instead", replaceWith = ReplaceWith("inContent(scene)")) -fun ElementMatcher.inScene(scene: SceneKey) = inContent(scene) +@Deprecated( + "Use `this and inContent()` instead", + replaceWith = ReplaceWith("this and inContent(scene)"), +) +fun ElementMatcher.inScene(scene: SceneKey) = this and inContent(scene) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index aa70a0ce156b..8613f6da0f62 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.pointer.AwaitPointerEventScope import androidx.compose.ui.input.pointer.PointerEvent import androidx.compose.ui.input.pointer.PointerEventPass +import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.PointerId import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope @@ -184,6 +185,7 @@ internal class MultiPointerDraggableNode( private var startedPosition: Offset? = null private var pointersDown: Int = 0 + private var isMouseWheel: Boolean = false internal fun pointersInfo(): PointersInfo { return PointersInfo( @@ -191,6 +193,7 @@ internal class MultiPointerDraggableNode( startedPosition = startedPosition, // We could have 0 pointers during fling or for other reasons. pointersDown = pointersDown.coerceAtLeast(1), + isMouseWheel = isMouseWheel, ) } @@ -202,8 +205,15 @@ internal class MultiPointerDraggableNode( // [requireAncestorPointersInfoOwner], to our descendants. while (currentContext.isActive) { // During the Initial pass, we receive the event after our ancestors. - val changes = awaitPointerEvent(PointerEventPass.Initial).changes + val pointerEvent = awaitPointerEvent(PointerEventPass.Initial) + + // Ignore cursor has entered the input region. + // This will only be sent after the cursor is hovering when in the input region. + if (pointerEvent.type == PointerEventType.Enter) continue + + val changes = pointerEvent.changes pointersDown = changes.countDown() + isMouseWheel = pointerEvent.type == PointerEventType.Scroll when { // There are no more pointers down. @@ -223,7 +233,8 @@ internal class MultiPointerDraggableNode( // The first pointer down, startedPosition was not set. startedPosition == null -> { - val firstPointerDown = changes.single() + // Mouse wheel could start with multiple pointer down + val firstPointerDown = changes.first() velocityPointerId = firstPointerDown.id velocityTracker.resetTracking() velocityTracker.addPointerInputChange(firstPointerDown) @@ -647,4 +658,8 @@ internal fun interface PointersInfoOwner { fun pointersInfo(): PointersInfo } -internal data class PointersInfo(val startedPosition: Offset?, val pointersDown: Int) +internal data class PointersInfo( + val startedPosition: Offset?, + val pointersDown: Int, + val isMouseWheel: Boolean, +) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 2657d7cf8156..a9a8668bd304 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -387,11 +387,11 @@ internal class MutableSceneTransitionLayoutStateImpl( transition.transformationSpec = transitions .transitionSpec(fromContent, toContent, key = transition.key) - .transformationSpec() + .transformationSpec(transition) transition.previewTransformationSpec = transitions .transitionSpec(fromContent, toContent, key = transition.key) - .previewTransformationSpec() + .previewTransformationSpec(transition) if (orientation != null) { transition.updateOverscrollSpecs( fromSpec = transitions.overscrollSpec(fromContent, orientation), @@ -430,6 +430,10 @@ internal class MutableSceneTransitionLayoutStateImpl( // Replace the transition. transitionStates = transitionStates.subList(0, transitionStates.lastIndex) + transition + + // Make sure it is removed from the finishedTransitions set if it was already + // finished. + finishedTransitions.remove(currentState) } else { // Append the new transition. transitionStates = transitionStates + transition diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt index 879dc542552c..8866fbfbf194 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.util.fastForEach +import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.transformation.AnchoredSize import com.android.compose.animation.scene.transformation.AnchoredTranslate import com.android.compose.animation.scene.transformation.DrawScale @@ -191,20 +192,21 @@ interface TransitionSpec { fun reversed(): TransitionSpec /** - * The [TransformationSpec] associated to this [TransitionSpec]. + * The [TransformationSpec] associated to this [TransitionSpec] for the given [transition]. * * Note that this is called once whenever a transition associated to this [TransitionSpec] is * started. */ - fun transformationSpec(): TransformationSpec + fun transformationSpec(transition: TransitionState.Transition): TransformationSpec /** - * The preview [TransformationSpec] associated to this [TransitionSpec]. + * The preview [TransformationSpec] associated to this [TransitionSpec] for the given + * [transition]. * * Note that this is called once whenever a transition associated to this [TransitionSpec] is * started. */ - fun previewTransformationSpec(): TransformationSpec? + fun previewTransformationSpec(transition: TransitionState.Transition): TransformationSpec? } interface TransformationSpec { @@ -241,7 +243,7 @@ interface TransformationSpec { distance = null, transformations = emptyList(), ) - internal val EmptyProvider = { Empty } + internal val EmptyProvider = { _: TransitionState.Transition -> Empty } } } @@ -249,9 +251,13 @@ internal class TransitionSpecImpl( override val key: TransitionKey?, override val from: ContentKey?, override val to: ContentKey?, - private val previewTransformationSpec: (() -> TransformationSpecImpl)? = null, - private val reversePreviewTransformationSpec: (() -> TransformationSpecImpl)? = null, - private val transformationSpec: () -> TransformationSpecImpl, + private val previewTransformationSpec: + ((TransitionState.Transition) -> TransformationSpecImpl)? = + null, + private val reversePreviewTransformationSpec: + ((TransitionState.Transition) -> TransformationSpecImpl)? = + null, + private val transformationSpec: (TransitionState.Transition) -> TransformationSpecImpl, ) : TransitionSpec { override fun reversed(): TransitionSpecImpl { return TransitionSpecImpl( @@ -260,8 +266,8 @@ internal class TransitionSpecImpl( to = from, previewTransformationSpec = reversePreviewTransformationSpec, reversePreviewTransformationSpec = previewTransformationSpec, - transformationSpec = { - val reverse = transformationSpec.invoke() + transformationSpec = { transition -> + val reverse = transformationSpec.invoke(transition) TransformationSpecImpl( progressSpec = reverse.progressSpec, swipeSpec = reverse.swipeSpec, @@ -272,10 +278,13 @@ internal class TransitionSpecImpl( ) } - override fun transformationSpec(): TransformationSpecImpl = this.transformationSpec.invoke() + override fun transformationSpec( + transition: TransitionState.Transition + ): TransformationSpecImpl = transformationSpec.invoke(transition) - override fun previewTransformationSpec(): TransformationSpecImpl? = - previewTransformationSpec?.invoke() + override fun previewTransformationSpec( + transition: TransitionState.Transition + ): TransformationSpecImpl? = previewTransformationSpec?.invoke(transition) } /** The definition of the overscroll behavior of the [content]. */ diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index 061583a64702..a3f2a434cff7 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -81,16 +81,16 @@ private class SwipeToSceneRootNode( draggableHandler: DraggableHandlerImpl, swipeDetector: SwipeDetector, ) : DelegatingNode() { - private var delegate = delegate(SwipeToSceneNode(draggableHandler, swipeDetector)) + private var delegateNode = delegate(SwipeToSceneNode(draggableHandler, swipeDetector)) fun update(draggableHandler: DraggableHandlerImpl, swipeDetector: SwipeDetector) { - if (draggableHandler == delegate.draggableHandler) { + if (draggableHandler == delegateNode.draggableHandler) { // Simple update, just update the swipe detector directly and keep the node. - delegate.swipeDetector = swipeDetector + delegateNode.swipeDetector = swipeDetector } else { // The draggableHandler changed, force recreate the underlying SwipeToSceneNode. - undelegate(delegate) - delegate = delegate(SwipeToSceneNode(draggableHandler, swipeDetector)) + undelegate(delegateNode) + delegateNode = delegate(SwipeToSceneNode(draggableHandler, swipeDetector)) } } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt index 763dc6bf49e0..e825c6e271ed 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt @@ -156,6 +156,9 @@ interface BaseTransitionBuilder : PropertyTransformationBuilder { @TransitionDsl interface TransitionBuilder : BaseTransitionBuilder { + /** The [TransitionState.Transition] for which we currently compute the transformations. */ + val transition: TransitionState.Transition + /** * The [AnimationSpec] used to animate the associated transition progress from `0` to `1` when * the transition is triggered (i.e. it is not gesture-based). diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt index 7ec5e4f4f149..a5ad999f7a64 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt @@ -27,6 +27,7 @@ import androidx.compose.animation.core.spring import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.Dp +import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.transformation.AnchoredSize import com.android.compose.animation.scene.transformation.AnchoredTranslate import com.android.compose.animation.scene.transformation.DrawScale @@ -128,8 +129,11 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { reversePreview: (TransitionBuilder.() -> Unit)?, builder: TransitionBuilder.() -> Unit, ): TransitionSpec { - fun transformationSpec(builder: TransitionBuilder.() -> Unit): TransformationSpecImpl { - val impl = TransitionBuilderImpl().apply(builder) + fun transformationSpec( + transition: TransitionState.Transition, + builder: TransitionBuilder.() -> Unit, + ): TransformationSpecImpl { + val impl = TransitionBuilderImpl(transition).apply(builder) return TransformationSpecImpl( progressSpec = impl.spec, swipeSpec = impl.swipeSpec, @@ -138,17 +142,15 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { ) } - val previewTransformationSpec = preview?.let { { transformationSpec(it) } } - val reversePreviewTransformationSpec = reversePreview?.let { { transformationSpec(it) } } - val transformationSpec = { transformationSpec(builder) } val spec = TransitionSpecImpl( key, from, to, - previewTransformationSpec, - reversePreviewTransformationSpec, - transformationSpec, + previewTransformationSpec = preview?.let { { t -> transformationSpec(t, it) } }, + reversePreviewTransformationSpec = + reversePreview?.let { { t -> transformationSpec(t, it) } }, + transformationSpec = { t -> transformationSpec(t, builder) }, ) transitionSpecs.add(spec) return spec @@ -227,7 +229,8 @@ internal abstract class BaseTransitionBuilderImpl : BaseTransitionBuilder { } } -internal class TransitionBuilderImpl : BaseTransitionBuilderImpl(), TransitionBuilder { +internal class TransitionBuilderImpl(override val transition: TransitionState.Transition) : + BaseTransitionBuilderImpl(), TransitionBuilder { override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow) override var swipeSpec: SpringSpec<Float>? = null override var distance: UserActionDistance? = null diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index 57b9423e85d1..a0fed9064181 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -127,7 +127,7 @@ class DraggableHandlerTest { val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal) var pointerInfoOwner: () -> PointersInfo = { - PointersInfo(startedPosition = Offset.Zero, pointersDown = 1) + PointersInfo(startedPosition = Offset.Zero, pointersDown = 1, isMouseWheel = false) } fun nestedScrollConnection( @@ -1187,7 +1187,9 @@ class DraggableHandlerTest { val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) // Drag from the **top** of the screen - pointerInfoOwner = { PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1) } + pointerInfoOwner = { + PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = false) + } assertIdle(currentScene = SceneC) nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) @@ -1205,7 +1207,11 @@ class DraggableHandlerTest { // Drag from the **bottom** of the screen pointerInfoOwner = { - PointersInfo(startedPosition = Offset(0f, SCREEN_SIZE), pointersDown = 1) + PointersInfo( + startedPosition = Offset(0f, SCREEN_SIZE), + pointersDown = 1, + isMouseWheel = false, + ) } assertIdle(currentScene = SceneC) @@ -1220,6 +1226,22 @@ class DraggableHandlerTest { } @Test + fun ignoreMouseWheel() = runGestureTest { + // Start at scene C. + navigateToSceneC() + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) + + // Use mouse wheel + pointerInfoOwner = { + PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = true) + } + assertIdle(currentScene = SceneC) + + nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) + assertIdle(currentScene = SceneC) + } + + @Test fun transitionIsImmediatelyUpdatedWhenReleasingFinger() = runGestureTest { // Swipe up from the middle to transition to scene B. val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementMatcherTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementMatcherTest.kt new file mode 100644 index 000000000000..af0962361fb2 --- /dev/null +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementMatcherTest.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.compose.animation.scene + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compose.animation.scene.TestElements.Bar +import com.android.compose.animation.scene.TestElements.Foo +import com.android.compose.animation.scene.TestScenes.SceneA +import com.android.compose.animation.scene.TestScenes.SceneB +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ElementMatcherTest { + @Test + fun and() { + val matcher = Foo and inContent(SceneA) + assertThat(matcher.matches(Foo, SceneA)).isTrue() + assertThat(matcher.matches(Foo, SceneB)).isFalse() + assertThat(matcher.matches(Bar, SceneA)).isFalse() + assertThat(matcher.matches(Bar, SceneB)).isFalse() + } + + @Test + fun or() { + val matcher = Foo or inContent(SceneA) + assertThat(matcher.matches(Foo, SceneA)).isTrue() + assertThat(matcher.matches(Foo, SceneB)).isTrue() + assertThat(matcher.matches(Bar, SceneA)).isTrue() + assertThat(matcher.matches(Bar, SceneB)).isFalse() + } + + @Test + fun not() { + val matcher = !Foo + assertThat(matcher.matches(Foo, SceneA)).isFalse() + assertThat(matcher.matches(Foo, SceneB)).isFalse() + assertThat(matcher.matches(Bar, SceneA)).isTrue() + assertThat(matcher.matches(Bar, SceneB)).isTrue() + } +} diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index 39d46990dc4b..ee807e6a7ede 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -2221,8 +2221,8 @@ class ElementTest { // In A => B, Foo is not shared and first fades out from A then fades in // B. sharedElement(TestElements.Foo, enabled = false) - fractionRange(end = 0.5f) { fade(TestElements.Foo.inContent(SceneA)) } - fractionRange(start = 0.5f) { fade(TestElements.Foo.inContent(SceneB)) } + fractionRange(end = 0.5f) { fade(TestElements.Foo.inScene(SceneA)) } + fractionRange(start = 0.5f) { fade(TestElements.Foo.inScene(SceneB)) } } from(SceneB, to = SceneA) { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt index cae6617cb11b..7ea414d6b8cd 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.assertPositionInRootIsEqualTo @@ -205,7 +206,8 @@ class OverlayTest { val key = MovableElementKey("MovableBar", contents = setOf(SceneA, OverlayA, OverlayB)) val elementChildTag = "elementChildTag" - fun elementChild(content: ContentKey) = hasTestTag(elementChildTag) and inContent(content) + fun elementChild(content: ContentKey) = + hasTestTag(elementChildTag) and SemanticsMatcher.inContent(content) @Composable fun ContentScope.MovableBar() { @@ -773,7 +775,7 @@ class OverlayTest { // Overscroll on Overlay A. scope.launch { state.startTransition(transition(SceneA, OverlayA, progress = { 1.5f })) } rule - .onNode(hasTestTag(movableElementChildTag) and inContent(SceneA)) + .onNode(hasTestTag(movableElementChildTag) and SemanticsMatcher.inContent(SceneA)) .assertPositionInRootIsEqualTo(0.dp, 0.dp) .assertSizeIsEqualTo(100.dp) .assertIsDisplayed() diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index f3a34884c756..f5bb5ba032c2 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -727,4 +727,45 @@ class SceneTransitionLayoutStateTest { // The previous job is cancelled and does not infinitely collect the progress. job.join() } + + @Test + fun replacedTransitionIsRemovedFromFinishedTransitions() = runTest { + val state = MutableSceneTransitionLayoutState(SceneA) + + val aToB = + transition( + SceneA, + SceneB, + onFreezeAndAnimate = { + // Do nothing, so that this transition stays in the transitionStates list and we + // can finish() it manually later. + }, + ) + val replacingAToB = transition(SceneB, SceneC) + val replacingBToC = transition(SceneB, SceneC, replacedTransition = replacingAToB) + + // Start A => B. + val aToBJob = state.startTransitionImmediately(animationScope = this, aToB) + + // Start B => C and immediately finish it. It will be flagged as finished in + // STLState.finishedTransitions given that A => B is not finished yet. + val bToCJob = state.startTransitionImmediately(animationScope = this, replacingAToB) + replacingAToB.finish() + bToCJob.join() + + // Start a new B => C that replaces the previously finished B => C. + val replacingBToCJob = + state.startTransitionImmediately(animationScope = this, replacingBToC) + + // Finish A => B. + aToB.finish() + aToBJob.join() + + // Finish the new B => C. + replacingBToC.finish() + replacingBToCJob.join() + + assertThat(state.transitionState).isIdle() + assertThat(state.transitionState).hasCurrentScene(SceneC) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 28d0a473935d..3001505d0e02 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -17,6 +17,8 @@ package com.android.compose.animation.scene import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.rememberScrollableState +import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -39,12 +41,14 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.ScrollWheel import androidx.compose.ui.test.assertPositionInRootIsEqualTo import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performMouseInput import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeRight import androidx.compose.ui.test.swipeUp @@ -102,26 +106,22 @@ class SwipeToSceneTest { modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName), ) { scene( - SceneA, + key = SceneA, userActions = if (swipesEnabled()) - mapOf( - Swipe.Left to SceneB, - Swipe.Down to TestScenes.SceneC, - Swipe.Up to SceneB, - ) + mapOf(Swipe.Left to SceneB, Swipe.Down to SceneC, Swipe.Up to SceneB) else emptyMap(), ) { Box(Modifier.fillMaxSize()) } scene( - SceneB, + key = SceneB, userActions = if (swipesEnabled()) mapOf(Swipe.Right to SceneA) else emptyMap(), ) { Box(Modifier.fillMaxSize()) } scene( - TestScenes.SceneC, + key = SceneC, userActions = if (swipesEnabled()) mapOf( @@ -196,7 +196,7 @@ class SwipeToSceneTest { // Drag is in progress, so currentScene = SceneA and progress = 56dp / LayoutHeight transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) - assertThat(transition).hasToScene(TestScenes.SceneC) + assertThat(transition).hasToScene(SceneC) assertThat(transition).hasCurrentScene(SceneA) assertThat(transition).hasProgress(56.dp / LayoutHeight) assertThat(transition).isInitiatedByUserInput() @@ -206,15 +206,15 @@ class SwipeToSceneTest { rule.onRoot().performTouchInput { up() } transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) - assertThat(transition).hasToScene(TestScenes.SceneC) - assertThat(transition).hasCurrentScene(TestScenes.SceneC) + assertThat(transition).hasToScene(SceneC) + assertThat(transition).hasCurrentScene(SceneC) assertThat(transition).hasProgress(56.dp / LayoutHeight) assertThat(transition).isInitiatedByUserInput() // Wait for the animation to finish. We should now be in scene C. rule.waitForIdle() assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) } @Test @@ -271,20 +271,20 @@ class SwipeToSceneTest { // We should be animating to C (currentScene = SceneC). transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) - assertThat(transition).hasToScene(TestScenes.SceneC) - assertThat(transition).hasCurrentScene(TestScenes.SceneC) + assertThat(transition).hasToScene(SceneC) + assertThat(transition).hasCurrentScene(SceneC) assertThat(transition).hasProgress(55.dp / LayoutHeight) // Wait for the animation to finish. We should now be in scene C. rule.waitForIdle() assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) } @Test fun multiPointerSwipe() { // Start at scene C. - val layoutState = layoutState(TestScenes.SceneC) + val layoutState = layoutState(SceneC) // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. @@ -295,7 +295,7 @@ class SwipeToSceneTest { } assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) // Swipe down with two fingers. rule.onRoot().performTouchInput { @@ -307,7 +307,7 @@ class SwipeToSceneTest { // We are transitioning to B because we used 2 fingers. val transition = assertThat(layoutState.transitionState).isSceneTransition() - assertThat(transition).hasFromScene(TestScenes.SceneC) + assertThat(transition).hasFromScene(SceneC) assertThat(transition).hasToScene(SceneB) // Release the fingers and wait for the animation to end. We are back to C because we only @@ -315,13 +315,13 @@ class SwipeToSceneTest { rule.onRoot().performTouchInput { repeat(2) { i -> up(pointerId = i) } } rule.waitForIdle() assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) } @Test fun defaultEdgeSwipe() { // Start at scene C. - val layoutState = layoutState(TestScenes.SceneC) + val layoutState = layoutState(SceneC) // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. @@ -332,7 +332,7 @@ class SwipeToSceneTest { } assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) // Swipe down from the top edge. rule.onRoot().performTouchInput { @@ -342,7 +342,7 @@ class SwipeToSceneTest { // We are transitioning to B (and not A) because we started from the top edge. var transition = assertThat(layoutState.transitionState).isSceneTransition() - assertThat(transition).hasFromScene(TestScenes.SceneC) + assertThat(transition).hasFromScene(SceneC) assertThat(transition).hasToScene(SceneB) // Release the fingers and wait for the animation to end. We are back to C because we only @@ -350,7 +350,7 @@ class SwipeToSceneTest { rule.onRoot().performTouchInput { up() } rule.waitForIdle() assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) // Swipe right from the left edge. rule.onRoot().performTouchInput { @@ -360,7 +360,7 @@ class SwipeToSceneTest { // We are transitioning to B (and not A) because we started from the left edge. transition = assertThat(layoutState.transitionState).isSceneTransition() - assertThat(transition).hasFromScene(TestScenes.SceneC) + assertThat(transition).hasFromScene(SceneC) assertThat(transition).hasToScene(SceneB) // Release the fingers and wait for the animation to end. We are back to C because we only @@ -368,7 +368,7 @@ class SwipeToSceneTest { rule.onRoot().performTouchInput { up() } rule.waitForIdle() assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) } @Test @@ -434,7 +434,7 @@ class SwipeToSceneTest { // We should still correctly compute that we are swiping down to scene C. var transition = assertThat(layoutState.transitionState).isSceneTransition() - assertThat(transition).hasToScene(TestScenes.SceneC) + assertThat(transition).hasToScene(SceneC) // Release the finger, animating back to scene A. rule.onRoot().performTouchInput { up() } @@ -502,6 +502,89 @@ class SwipeToSceneTest { } @Test + fun mouseWheel_pointerInputApi_ignoredByStl() { + val layoutState = layoutState() + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + TestContent(layoutState) + } + + rule.onRoot().performMouseInput { + enter(middle) + scroll(touchSlop, ScrollWheel.Vertical) + } + + // Mouse wheel scroll are ignored + assertThat(layoutState.currentTransition).isNull() + } + + @Test + fun mouseWheel_scrollableCannotScroll_ignoredByStl() { + val layoutState = layoutState() + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) { + scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) { + Box( + Modifier.fillMaxSize() + // A scrollable that does not consume the scroll gesture + .scrollable(rememberScrollableState { 0f }, Orientation.Vertical) + ) + } + scene(SceneB) { Box(Modifier.fillMaxSize()) } + } + } + + rule.onRoot().performMouseInput { + enter(middle) + scroll(touchSlop, ScrollWheel.Vertical) + } + + // Mouse wheel scroll are ignored + assertThat(layoutState.currentTransition).isNull() + } + + @Test + fun mouseWheel_scrollableConsume_ignoredByStl() { + val layoutState = layoutState() + var touchSlop = 0f + var lastScroll = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) { + scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) { + Box( + Modifier.fillMaxSize() + // A scrollable that consumes the scroll gesture + .scrollable( + rememberScrollableState { + lastScroll = it + it + }, + Orientation.Vertical, + ) + ) + } + scene(SceneB) { Box(Modifier.fillMaxSize()) } + } + } + + rule.onRoot().performMouseInput { + enter(middle) + scroll(touchSlop * 10, ScrollWheel.Vertical) + } + + // Mouse wheel scroll are ignored + assertThat(layoutState.currentTransition).isNull() + + // Mouse wheel scroll can still be consumed by the scrollable + assertThat(lastScroll).isNotEqualTo(0f) + assertThat(touchSlop).isNotEqualTo(0f) + } + + @Test fun transitionKey() { val transitionkey = TransitionKey(debugName = "foo") val state = diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt index 223af8039fed..d66d6b3ab219 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt @@ -23,11 +23,17 @@ import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween import androidx.compose.foundation.gestures.Orientation import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compose.animation.scene.TestScenes.SceneA +import com.android.compose.animation.scene.TestScenes.SceneB +import com.android.compose.animation.scene.TestScenes.SceneC +import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.transformation.OverscrollTranslate import com.android.compose.animation.scene.transformation.Transformation import com.android.compose.animation.scene.transformation.TransformationRange +import com.android.compose.test.transition import com.google.common.truth.Correspondence import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith @@ -43,9 +49,9 @@ class TransitionDslTest { @Test fun manyTransitions() { val transitions = transitions { - from(TestScenes.SceneA, to = TestScenes.SceneB) - from(TestScenes.SceneB, to = TestScenes.SceneC) - from(TestScenes.SceneC, to = TestScenes.SceneA) + from(SceneA, to = SceneB) + from(SceneB, to = SceneC) + from(SceneC, to = SceneA) } assertThat(transitions.transitionSpecs).hasSize(3) } @@ -53,9 +59,9 @@ class TransitionDslTest { @Test fun toFromBuilders() { val transitions = transitions { - from(TestScenes.SceneA, to = TestScenes.SceneB) - from(TestScenes.SceneB) - to(TestScenes.SceneC) + from(SceneA, to = SceneB) + from(SceneB) + to(SceneC) } assertThat(transitions.transitionSpecs) @@ -65,38 +71,34 @@ class TransitionDslTest { "has (from, to) equal to", ) ) - .containsExactly( - TestScenes.SceneA to TestScenes.SceneB, - TestScenes.SceneB to null, - null to TestScenes.SceneC, - ) + .containsExactly(SceneA to SceneB, SceneB to null, null to SceneC) } + private fun aToB() = transition(SceneA, SceneB) + @Test fun defaultTransitionSpec() { - val transitions = transitions { from(TestScenes.SceneA, to = TestScenes.SceneB) } - val transformationSpec = transitions.transitionSpecs.single().transformationSpec() + val transitions = transitions { from(SceneA, to = SceneB) } + val transformationSpec = transitions.transitionSpecs.single().transformationSpec(aToB()) assertThat(transformationSpec.progressSpec).isInstanceOf(SpringSpec::class.java) } @Test fun customTransitionSpec() { val transitions = transitions { - from(TestScenes.SceneA, to = TestScenes.SceneB) { spec = tween(durationMillis = 42) } + from(SceneA, to = SceneB) { spec = tween(durationMillis = 42) } } - val transformationSpec = transitions.transitionSpecs.single().transformationSpec() + val transformationSpec = transitions.transitionSpecs.single().transformationSpec(aToB()) assertThat(transformationSpec.progressSpec).isInstanceOf(TweenSpec::class.java) assertThat((transformationSpec.progressSpec as TweenSpec).durationMillis).isEqualTo(42) } @Test fun defaultRange() { - val transitions = transitions { - from(TestScenes.SceneA, to = TestScenes.SceneB) { fade(TestElements.Foo) } - } + val transitions = transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } } val transformations = - transitions.transitionSpecs.single().transformationSpec().transformations + transitions.transitionSpecs.single().transformationSpec(aToB()).transformations assertThat(transformations.size).isEqualTo(1) assertThat(transformations.single().range).isEqualTo(null) } @@ -104,7 +106,7 @@ class TransitionDslTest { @Test fun fractionRange() { val transitions = transitions { - from(TestScenes.SceneA, to = TestScenes.SceneB) { + from(SceneA, to = SceneB) { fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) } fractionRange(start = 0.2f) { fade(TestElements.Foo) } fractionRange(end = 0.9f) { fade(TestElements.Foo) } @@ -119,7 +121,7 @@ class TransitionDslTest { } val transformations = - transitions.transitionSpecs.single().transformationSpec().transformations + transitions.transitionSpecs.single().transformationSpec(aToB()).transformations assertThat(transformations) .comparingElementsUsing(TRANSFORMATION_RANGE) .containsExactly( @@ -133,7 +135,7 @@ class TransitionDslTest { @Test fun timestampRange() { val transitions = transitions { - from(TestScenes.SceneA, to = TestScenes.SceneB) { + from(SceneA, to = SceneB) { spec = tween(500) timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) } @@ -150,7 +152,7 @@ class TransitionDslTest { } val transformations = - transitions.transitionSpecs.single().transformationSpec().transformations + transitions.transitionSpecs.single().transformationSpec(aToB()).transformations assertThat(transformations) .comparingElementsUsing(TRANSFORMATION_RANGE) .containsExactly( @@ -168,7 +170,7 @@ class TransitionDslTest { @Test fun reversed() { val transitions = transitions { - from(TestScenes.SceneA, to = TestScenes.SceneB) { + from(SceneA, to = SceneB) { spec = tween(500) reversed { fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) } @@ -178,7 +180,7 @@ class TransitionDslTest { } val transformations = - transitions.transitionSpecs.single().transformationSpec().transformations + transitions.transitionSpecs.single().transformationSpec(aToB()).transformations assertThat(transformations) .comparingElementsUsing(TRANSFORMATION_RANGE) .containsExactly( @@ -191,8 +193,8 @@ class TransitionDslTest { fun defaultReversed() { val transitions = transitions { from( - TestScenes.SceneA, - to = TestScenes.SceneB, + SceneA, + to = SceneB, preview = { fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) } }, reversePreview = { fractionRange(start = 0.5f, end = 0.6f) { fade(TestElements.Foo) } @@ -206,10 +208,9 @@ class TransitionDslTest { // Fetch the transition from B to A, which will automatically reverse the transition from A // to B we defined. - val transitionSpec = - transitions.transitionSpec(from = TestScenes.SceneB, to = TestScenes.SceneA, key = null) + val transitionSpec = transitions.transitionSpec(from = SceneB, to = SceneA, key = null) - val transformations = transitionSpec.transformationSpec().transformations + val transformations = transitionSpec.transformationSpec(aToB()).transformations assertThat(transformations) .comparingElementsUsing(TRANSFORMATION_RANGE) @@ -218,7 +219,8 @@ class TransitionDslTest { TransformationRange(start = 1f - 300 / 500f, end = 1f - 100 / 500f), ) - val previewTransformations = transitionSpec.previewTransformationSpec()?.transformations + val previewTransformations = + transitionSpec.previewTransformationSpec(aToB())?.transformations assertThat(previewTransformations) .comparingElementsUsing(TRANSFORMATION_RANGE) @@ -229,8 +231,8 @@ class TransitionDslTest { fun defaultPredictiveBack() { val transitions = transitions { from( - TestScenes.SceneA, - to = TestScenes.SceneB, + SceneA, + to = SceneB, preview = { fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) } }, ) { spec = tween(500) @@ -243,12 +245,12 @@ class TransitionDslTest { // transition despite it not having the PredictiveBack key set. val transitionSpec = transitions.transitionSpec( - from = TestScenes.SceneA, - to = TestScenes.SceneB, + from = SceneA, + to = SceneB, key = TransitionKey.PredictiveBack, ) - val transformations = transitionSpec.transformationSpec().transformations + val transformations = transitionSpec.transformationSpec(aToB()).transformations assertThat(transformations) .comparingElementsUsing(TRANSFORMATION_RANGE) @@ -257,7 +259,8 @@ class TransitionDslTest { TransformationRange(start = 100 / 500f, end = 300 / 500f), ) - val previewTransformations = transitionSpec.previewTransformationSpec()?.transformations + val previewTransformations = + transitionSpec.previewTransformationSpec(aToB())?.transformations assertThat(previewTransformations) .comparingElementsUsing(TRANSFORMATION_RANGE) @@ -271,10 +274,10 @@ class TransitionDslTest { val transitions = transitions { defaultSwipeSpec = defaultSpec - from(TestScenes.SceneA, to = TestScenes.SceneB) { + from(SceneA, to = SceneB) { // Default swipe spec. } - from(TestScenes.SceneA, to = TestScenes.SceneC) { swipeSpec = specFromAToC } + from(SceneA, to = SceneC) { swipeSpec = specFromAToC } } assertThat(transitions.defaultSwipeSpec).isSameInstanceAs(defaultSpec) @@ -282,8 +285,8 @@ class TransitionDslTest { // A => B does not have a custom spec. assertThat( transitions - .transitionSpec(from = TestScenes.SceneA, to = TestScenes.SceneB, key = null) - .transformationSpec() + .transitionSpec(from = SceneA, to = SceneB, key = null) + .transformationSpec(aToB()) .swipeSpec ) .isNull() @@ -291,8 +294,8 @@ class TransitionDslTest { // A => C has a custom swipe spec. assertThat( transitions - .transitionSpec(from = TestScenes.SceneA, to = TestScenes.SceneC, key = null) - .transformationSpec() + .transitionSpec(from = SceneA, to = SceneC, key = null) + .transformationSpec(transition(from = SceneA, to = SceneC)) .swipeSpec ) .isSameInstanceAs(specFromAToC) @@ -301,7 +304,7 @@ class TransitionDslTest { @Test fun overscrollSpec() { val transitions = transitions { - overscroll(TestScenes.SceneA, Orientation.Vertical) { + overscroll(SceneA, Orientation.Vertical) { translate(TestElements.Bar, x = { 1f }, y = { 2f }) } } @@ -313,9 +316,7 @@ class TransitionDslTest { @Test fun overscrollSpec_for_overscrollDisabled() { - val transitions = transitions { - overscrollDisabled(TestScenes.SceneA, Orientation.Vertical) - } + val transitions = transitions { overscrollDisabled(SceneA, Orientation.Vertical) } val overscrollSpec = transitions.overscrollSpecs.single() assertThat(overscrollSpec.transformationSpec.transformations).isEmpty() } @@ -323,10 +324,24 @@ class TransitionDslTest { @Test fun overscrollSpec_throwIfTransformationsIsEmpty() { assertThrows(IllegalStateException::class.java) { - transitions { overscroll(TestScenes.SceneA, Orientation.Vertical) {} } + transitions { overscroll(SceneA, Orientation.Vertical) {} } } } + @Test + fun transitionIsPassedToBuilder() = runTest { + var transitionPassedToBuilder: TransitionState.Transition? = null + val state = + MutableSceneTransitionLayoutState( + SceneA, + transitions { from(SceneA, to = SceneB) { transitionPassedToBuilder = transition } }, + ) + + val transition = aToB() + state.startTransitionImmediately(animationScope = backgroundScope, transition) + assertThat(transitionPassedToBuilder).isSameInstanceAs(transition) + } + companion object { private val TRANSFORMATION_RANGE = Correspondence.transforming<Transformation, TransformationRange?>( diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt index 4877cd610875..2e3a934c2701 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt @@ -31,7 +31,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.TestElements import com.android.compose.animation.scene.TestScenes -import com.android.compose.animation.scene.inContent +import com.android.compose.animation.scene.inScene import com.android.compose.animation.scene.testTransition import com.android.compose.test.assertSizeIsEqualTo import org.junit.Rule @@ -125,10 +125,10 @@ class SharedElementTest { sharedElement(TestElements.Foo, enabled = false) // In SceneA, Foo leaves to the left edge. - translate(TestElements.Foo.inContent(TestScenes.SceneA), Edge.Left) + translate(TestElements.Foo.inScene(TestScenes.SceneA), Edge.Left) // In SceneB, Foo comes from the bottom edge. - translate(TestElements.Foo.inContent(TestScenes.SceneB), Edge.Bottom) + translate(TestElements.Foo.inScene(TestScenes.SceneB), Edge.Bottom) }, ) { before { onElement(TestElements.Foo).assertPositionInRootIsEqualTo(10.dp, 50.dp) } diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt index 22450d32ea62..b3201d0daffe 100644 --- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt +++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt @@ -25,11 +25,11 @@ fun isElement(element: ElementKey, content: ContentKey? = null): SemanticsMatche return if (content == null) { hasTestTag(element.testTag) } else { - hasTestTag(element.testTag) and inContent(content) + hasTestTag(element.testTag) and SemanticsMatcher.inContent(content) } } /** A [SemanticsMatcher] that matches anything inside [content]. */ -fun inContent(content: ContentKey): SemanticsMatcher { +fun SemanticsMatcher.Companion.inContent(content: ContentKey): SemanticsMatcher { return hasAnyAncestor(hasTestTag(content.testTag)) } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt index d001ef966c13..031fbabb239f 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt @@ -68,7 +68,10 @@ private constructor( seedColor = null, overrideChroma = null, typefaceCache = - TypefaceCache(messageBuffer) { Typeface.createFromAsset(pluginCtx.assets, it) }, + TypefaceCache(messageBuffer) { + // TODO(b/364680873): Move constant to config_clockFontFamily when shipping + return@TypefaceCache Typeface.create("google-sans-flex-clock", Typeface.NORMAL) + }, getThemeSeedColor = getThemeSeedColor ?: Companion::getThemeSeedColor, messageBuffer = messageBuffer, ) diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt index 3903dbaf64c6..900971bc3927 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt @@ -17,6 +17,8 @@ import android.content.Context import android.content.res.Resources import android.view.LayoutInflater import com.android.systemui.customization.R +import com.android.systemui.log.core.LogLevel +import com.android.systemui.log.core.LogcatOnlyMessageBuffer import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId import com.android.systemui.plugins.clocks.ClockMessageBuffers @@ -53,7 +55,9 @@ class DefaultClockProvider( } return if (clockReactiveVariants) { - val assets = AssetLoader(ctx, ctx, "clocks/", messageBuffers!!.infraMessageBuffer) + val buffer = + messageBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.INFO) + val assets = AssetLoader(ctx, ctx, "clocks/", buffer) FlexClockController(ctx, resources, assets, FLEX_DESIGN, messageBuffers) } else { DefaultClockController( diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt index ef24d2ad3071..9067fb094634 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt @@ -71,7 +71,7 @@ class FlexClockFaceController( val layer = face.layers[0] layerController = - if (isLargeClock) + if (isLargeClock) { ComposedDigitalLayerController( ctx, resources, @@ -79,7 +79,7 @@ class FlexClockFaceController( layer as ComposedDigitalHandLayer, messageBuffer, ) - else { + } else { val childView = SimpleDigitalClockTextView(ctx, messageBuffer) SimpleDigitalHandLayerController( ctx, diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md index 234c7a032d2e..bf15b4feadfb 100644 --- a/packages/SystemUI/docs/scene.md +++ b/packages/SystemUI/docs/scene.md @@ -63,7 +63,7 @@ the instructions below to turn it on. NOTE: in case these instructions become stale and don't actually enable the framework, please make sure `SceneContainerFlag.isEnabled` in the [`SceneContainerFlag.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt) -file evalutes to `true`. +file evaluates to `true`. 1. Set a collection of **aconfig flags** to `true` by running the following commands: @@ -73,7 +73,6 @@ file evalutes to `true`. $ adb shell device_config override systemui com.android.systemui.migrate_clocks_to_blueprint true $ adb shell device_config override systemui com.android.systemui.notification_avalanche_throttle_hun true $ adb shell device_config override systemui com.android.systemui.predictive_back_sysui true - $ adb shell device_config override systemui com.android.systemui.device_entry_udfps_refactor true $ adb shell device_config override systemui com.android.systemui.scene_container true ``` 2. **Restart** System UI by issuing the following command: diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java index 00c5577b8017..ce57fe256798 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java @@ -18,8 +18,6 @@ package com.android.keyguard; import static android.view.View.INVISIBLE; -import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.mock; @@ -171,7 +169,6 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase { when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); mExecutor = new FakeExecutor(new FakeSystemClock()); mFakeFeatureFlags = new FakeFeatureFlags(); - mFakeFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); mController = new KeyguardClockSwitchController( mView, mStatusBarStateController, @@ -188,7 +185,6 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase { mLogBuffer, KeyguardInteractorFactory.create(mFakeFeatureFlags).getKeyguardInteractor(), mKeyguardClockInteractor, - mFakeFeatureFlags, mock(InWindowLauncherUnlockAnimationManager.class) ); diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt deleted file mode 100644 index c2c0f5713d9b..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard - -import android.view.View -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class KeyguardClockSwitchControllerWithCoroutinesTest : KeyguardClockSwitchControllerBaseTest() { - - @Test - fun testStatusAreaVisibility_onLockscreenHostedDreamStateChanged() = - runBlocking(IMMEDIATE) { - // GIVEN starting state for the keyguard clock and wallpaper dream enabled - mFakeFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true) - init() - - // WHEN dreaming starts - mController.mIsActiveDreamLockscreenHostedCallback.accept( - true /* isActiveDreamLockscreenHosted */ - ) - - // THEN the status area is hidden - mExecutor.runAllReady() - assertEquals(View.INVISIBLE, mStatusArea.visibility) - - // WHEN dreaming stops - mController.mIsActiveDreamLockscreenHostedCallback.accept( - false /* isActiveDreamLockscreenHosted */ - ) - mExecutor.runAllReady() - - // THEN status area view is visible - assertEquals(View.VISIBLE, mStatusArea.visibility) - } - - companion object { - private val IMMEDIATE = Dispatchers.Main.immediate - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java index 662815ee7cbe..fcb433b5db4e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java @@ -46,7 +46,9 @@ import android.platform.test.annotations.EnableFlags; import android.provider.Settings; import android.testing.TestableLooper; import android.view.View; +import android.view.ViewGroup; import android.widget.LinearLayout; +import android.widget.Space; import android.widget.Spinner; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -226,7 +228,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { mDialog.show(); LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog); - assertThat(relatedToolsView.getChildCount()).isEqualTo(1); + assertThat(countChildWithoutSpace(relatedToolsView)).isEqualTo(1); } @Test @@ -244,12 +246,13 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { when(mActivityInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL); when(mActivityInfo.loadIcon(mPackageManager)).thenReturn(mDrawable); when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT); + when(mDrawable.mutate()).thenReturn(mDrawable); setUpPairNewDeviceDialog(); mDialog.show(); LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog); - assertThat(relatedToolsView.getChildCount()).isEqualTo(2); + assertThat(countChildWithoutSpace(relatedToolsView)).isEqualTo(2); } @Test @@ -364,6 +367,16 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { return dialog.requireViewById(R.id.preset_spinner); } + private int countChildWithoutSpace(ViewGroup viewGroup) { + int spaceCount = 0; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + if (viewGroup.getChildAt(i) instanceof Space) { + spaceCount++; + } + } + return viewGroup.getChildCount() - spaceCount; + } + @After public void reset() { if (mDialogDelegate != null) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java index 17ce1ddee87a..77369f7ec881 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java @@ -85,7 +85,7 @@ public class HearingDevicesToolItemParserTest extends SysuiTestCase { } @Test - public void parseStringArray_noString_emptyResult() { + public void parseStringArray_noToolName_emptyResult() { assertThat(HearingDevicesToolItemParser.parseStringArray(mContext, new String[]{}, new String[]{})).isEqualTo(emptyList()); } @@ -103,14 +103,14 @@ public class HearingDevicesToolItemParserTest extends SysuiTestCase { } @Test - public void parseStringArray_fourToolName_maxThreeToolItem() { + public void parseStringArray_threeToolNames_maxTwoToolItems() { String componentNameString = TEST_PKG + "/" + TEST_CLS; - String[] fourToolName = - new String[]{componentNameString, componentNameString, componentNameString, - componentNameString}; + String[] threeToolNames = + new String[]{componentNameString, componentNameString, componentNameString}; List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext, - fourToolName, new String[]{}); + threeToolNames, new String[]{}); + assertThat(toolItemList.size()).isEqualTo(HearingDevicesToolItemParser.MAX_NUM); } @@ -120,6 +120,7 @@ public class HearingDevicesToolItemParserTest extends SysuiTestCase { List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext, wrongFormatToolName, new String[]{}); + assertThat(toolItemList.size()).isEqualTo(0); } @@ -129,6 +130,7 @@ public class HearingDevicesToolItemParserTest extends SysuiTestCase { List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext, notExistToolName, new String[]{}); + assertThat(toolItemList.size()).isEqualTo(0); } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt deleted file mode 100644 index 13306becf6d2..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.testing.TestableLooper -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.phone.SystemUIDialogManager -import org.junit.Assert.assertFalse -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.junit.MockitoJUnit - -@SmallTest -@RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper -class UdfpsBpViewControllerTest : SysuiTestCase() { - - @JvmField @Rule var rule = MockitoJUnit.rule() - - @Mock lateinit var udfpsBpView: UdfpsBpView - @Mock lateinit var statusBarStateController: StatusBarStateController - @Mock lateinit var shadeInteractor: ShadeInteractor - @Mock lateinit var systemUIDialogManager: SystemUIDialogManager - @Mock lateinit var dumpManager: DumpManager - @Mock lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor - - private lateinit var udfpsBpViewController: UdfpsBpViewController - - @Before - fun setup() { - udfpsBpViewController = - UdfpsBpViewController( - udfpsBpView, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - - @TestableLooper.RunWithLooper(setAsMainLooper = true) - @Test - fun testShouldNeverPauseAuth() { - assertFalse(udfpsBpViewController.shouldPauseAuth()) - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 437a4b357372..21c6583d4e84 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -16,34 +16,17 @@ package com.android.systemui.biometrics; -import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; -import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START; -import static android.view.MotionEvent.ACTION_DOWN; -import static android.view.MotionEvent.ACTION_MOVE; -import static android.view.MotionEvent.ACTION_UP; - -import static com.android.internal.util.FunctionalUtils.ThrowingConsumer; -import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.graphics.Rect; -import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricRequestConstants; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.SensorProperties; @@ -58,13 +41,9 @@ import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; import android.testing.TestableLooper.RunWithLooper; -import android.util.Pair; -import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.Surface; import android.view.View; -import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityManager; @@ -75,15 +54,11 @@ import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; -import com.android.systemui.biometrics.udfps.InteractionEvent; -import com.android.systemui.biometrics.udfps.NormalizedTouchData; import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor; -import com.android.systemui.biometrics.udfps.TouchProcessorResult; import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel; import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -102,7 +77,6 @@ import com.android.systemui.power.data.repository.FakePowerRepository; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.power.shared.model.WakeSleepReason; import com.android.systemui.power.shared.model.WakefulnessState; -import com.android.systemui.res.R; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.VibratorHelper; @@ -130,7 +104,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; -import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -204,16 +177,6 @@ public class UdfpsControllerTest extends SysuiTestCase { private FeatureFlags mFeatureFlags; // Stuff for configuring mocks @Mock - private UdfpsView mUdfpsView; - @Mock - private UdfpsBpView mBpView; - @Mock - private UdfpsFpmEmptyView mFpmEmptyView; - @Mock - private UdfpsKeyguardViewLegacy mKeyguardView; - private final UdfpsAnimationViewController mUdfpsKeyguardViewController = - mock(UdfpsKeyguardViewControllerLegacy.class); - @Mock private SystemUIDialogManager mSystemUIDialogManager; @Mock private ActivityTransitionAnimator mActivityTransitionAnimator; @@ -241,8 +204,6 @@ public class UdfpsControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<View> mViewCaptor; @Captor - private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor; - @Captor private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor; @Captor private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor; @@ -287,18 +248,9 @@ public class UdfpsControllerTest extends SysuiTestCase { mContext.getOrCreateTestableResources() .addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false); - when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)) - .thenReturn(mUdfpsView); - when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view_legacy, null)) - .thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD - when(mLayoutInflater.inflate(R.layout.udfps_bp_view, null)) - .thenReturn(mBpView); - when(mLayoutInflater.inflate(R.layout.udfps_fpm_empty_view, null)) - .thenReturn(mFpmEmptyView); when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); when(mSessionTracker.getSessionId(anyInt())).thenReturn( (new InstanceIdSequence(1 << 20)).newInstanceId()); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */, @@ -327,7 +279,6 @@ public class UdfpsControllerTest extends SysuiTestCase { // Create a fake background executor. mBiometricExecutor = new FakeExecutor(new FakeSystemClock()); - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); initUdfpsController(mOpticalProps); } @@ -349,7 +300,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mFalsingManager, mPowerManager, mAccessibilityManager, - mLockscreenShadeTransitionController, mScreenLifecycle, mVibrator, mUdfpsHapticsSimulator, @@ -390,101 +340,6 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void dozeTimeTick() throws RemoteException { - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - mUdfpsController.dozeTimeTick(); - verify(mUdfpsView).dozeTimeTick(); - } - - @Test - public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { - // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = obtainMotionEvent(ACTION_DOWN, 0, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); - } - - @Test - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { - onActionMoveTouch_whenCanDismissLockScreen_entersDevice(false /* stale */); - } - - @Test - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice_ignoreStale() - throws RemoteException { - onActionMoveTouch_whenCanDismissLockScreen_entersDevice(true /* stale */); - } - - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice(boolean stale) - throws RemoteException { - // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_MOVE is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - if (stale) { - mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId); - mFgExecutor.runAllReady(); - } - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager, stale ? never() : times(1)) - .notifyKeyguardAuthenticated(anyBoolean()); - } - - @Test - public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException { - // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false); - - // GIVEN that the overlay is showing - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); - } - - @Test public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException { // GIVEN overlay was showing and the udfps bouncer is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, @@ -524,61 +379,6 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void updateOverlayParams_recreatesOverlay_ifParamsChanged() throws Exception { - final Rect[] sensorBounds = new Rect[]{new Rect(10, 10, 20, 20), new Rect(5, 5, 25, 25)}; - final int[] displayWidth = new int[]{1080, 1440}; - final int[] displayHeight = new int[]{1920, 2560}; - final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]}; - final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90}; - final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0], - sensorBounds[0], displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0], - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); - - for (int i1 = 0; i1 <= 1; ++i1) { - for (int i2 = 0; i2 <= 1; ++i2) { - for (int i3 = 0; i3 <= 1; ++i3) { - for (int i4 = 0; i4 <= 1; ++i4) { - for (int i5 = 0; i5 <= 1; ++i5) { - final UdfpsOverlayParams newParams = new UdfpsOverlayParams( - sensorBounds[i1], sensorBounds[i1], displayWidth[i2], - displayHeight[i3], scaleFactor[i4], rotation[i5], - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); - - if (newParams.equals(oldParams)) { - continue; - } - - // Initialize the overlay with old parameters. - mUdfpsController.updateOverlayParams(mOpticalProps, oldParams); - - // Show the overlay. - reset(mWindowManager); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, - mOpticalProps.sensorId, - BiometricRequestConstants.REASON_ENROLL_ENROLLING, - mUdfpsOverlayControllerCallback); - - mFgExecutor.runAllReady(); - verify(mWindowManager).addView(mViewCaptor.capture(), any()); - when(mViewCaptor.getValue().getParent()) - .thenReturn(mock(ViewGroup.class)); - - // Update overlay parameters. - reset(mWindowManager); - mUdfpsController.updateOverlayParams(mOpticalProps, newParams); - mFgExecutor.runAllReady(); - - // Ensure the overlay was recreated. - verify(mWindowManager).removeView(any()); - verify(mWindowManager).addView(any(), any()); - } - } - } - } - } - } - - @Test public void updateOverlayParams_doesNothing_ifParamsDidntChange() throws Exception { final Rect sensorBounds = new Rect(10, 10, 20, 20); final int displayWidth = 1080; @@ -595,7 +395,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); - verify(mWindowManager).addView(any(), any()); // Update overlay with the same parameters. mUdfpsController.updateOverlayParams(mOpticalProps, @@ -606,870 +405,4 @@ public class UdfpsControllerTest extends SysuiTestCase { // Ensure the overlay was not recreated. verify(mWindowManager, never()).removeView(any()); } - - private static MotionEvent obtainMotionEvent(int action, float x, float y, float minor, - float major) { - MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties(); - pp.id = 1; - MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords(); - pc.x = x; - pc.y = y; - pc.touchMinor = minor; - pc.touchMajor = major; - return MotionEvent.obtain(0, 0, action, 1, new MotionEvent.PointerProperties[]{pp}, - new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0); - } - - private static class TestParams { - public final FingerprintSensorPropertiesInternal sensorProps; - - TestParams(FingerprintSensorPropertiesInternal sensorProps) { - this.sensorProps = sensorProps; - } - } - - private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) { - for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps, - mUltrasonicProps)) { - initUdfpsController(sensorProps); - testParamsConsumer.accept(new TestParams(sensorProps)); - } - } - - @Test - public void onTouch_propagatesTouchInNativeOrientationAndResolution() { - runWithAllParams( - this::onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized); - } - - private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized( - TestParams testParams) throws RemoteException { - reset(mUdfpsView); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner. - final int pointerId = 0; - final int displayWidth = 1080; - final int displayHeight = 1920; - final float scaleFactor = 1f; // This means the native resolution is 1440x2560. - final float touchMinor = 10f; - final float touchMajor = 20f; - final float orientation = 30f; - - // Expecting a touch at the very bottom right corner in native orientation and resolution. - final float expectedX = displayWidth / scaleFactor; - final float expectedY = displayHeight / scaleFactor; - final float expectedMinor = touchMinor / scaleFactor; - final float expectedMajor = touchMajor / scaleFactor; - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // GIVEN a valid touch on sensor - NormalizedTouchData touchData = new NormalizedTouchData(pointerId, displayWidth, - displayHeight, touchMinor, touchMajor, orientation, 0L, 0L); - TouchProcessorResult processorDownResult = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.DOWN, 1, touchData); - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorDownResult); - - // Show the overlay. - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // Test ROTATION_0 - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_0, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, - touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - - // Test ROTATION_90 - reset(mFingerprintManager); - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_90, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - - // Test ROTATION_270 - reset(mFingerprintManager); - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_270, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - - // Test ROTATION_180 - reset(mFingerprintManager); - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_180, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - // ROTATION_180 is not supported. It should be treated like ROTATION_0. - event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void fingerDown() { - runWithAllParams(this::fingerDownParameterized); - } - - private void fingerDownParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView, mFingerprintManager, mLatencyTracker, - mKeyguardUpdateMonitor); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.DOWN, 1 /* pointerId */, touchData); - - initUdfpsController(testParams.sensorProps); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - // WHEN ACTION_DOWN is received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN the touch provider is notified about onPointerDown. - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); - - // AND display configuration begins - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture()); - } else { - verify(mLatencyTracker, never()).onActionStart( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - verify(mUdfpsView, never()).configureDisplay(any()); - } - verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // AND onDisplayConfigured notifies FingerprintManager about onUiReady - mOnDisplayConfiguredCaptor.getValue().run(); - mBiometricExecutor.runAllReady(); - InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker); - inOrder.verify(mFingerprintManager).onUdfpsUiEvent( - eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId)); - inOrder.verify(mLatencyTracker).onActionEnd( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - } else { - verify(mFingerprintManager, never()).onUdfpsUiEvent( - eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt()); - verify(mLatencyTracker, never()).onActionEnd( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - } - } - - @Test - public void aodInterrupt() { - runWithAllParams(this::aodInterruptParameterized); - } - - private void aodInterruptParameterized(TestParams testParams) throws RemoteException { - mUdfpsController.cancelAodSendFingerUpAction(); - reset(mUdfpsView, mFingerprintManager, mKeyguardUpdateMonitor); - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - // GIVEN that the overlay is showing and screen is on and fp is running - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - // WHEN fingerprint is requested because of AOD interrupt - mUdfpsController.onAodInterrupt(0, 0, 2f, 3f); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // THEN display configuration begins - // AND onDisplayConfigured notifies FingerprintManager about onUiReady - verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture()); - mOnDisplayConfiguredCaptor.getValue().run(); - } else { - verify(mUdfpsView, never()).configureDisplay(mOnDisplayConfiguredCaptor.capture()); - } - mBiometricExecutor.runAllReady(); - - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); - } - - @Test - public void tryAodSendFingerUp_displayConfigurationChanges() { - runWithAllParams(this::cancelAodInterruptParameterized); - } - - private void cancelAodInterruptParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView); - - // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - // WHEN it is cancelled - mUdfpsController.tryAodSendFingerUp(); - // THEN the display is unconfigured - verify(mUdfpsView).unconfigureDisplay(); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - // WHEN it is cancelled - mUdfpsController.tryAodSendFingerUp(); - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void onFingerUp_displayConfigurationChange() { - runWithAllParams(this::onFingerUp_displayConfigurationParameterized); - } - - private void onFingerUp_displayConfigurationParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // GIVEN AOD interrupt - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - - // WHEN up-action received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - mFgExecutor.runAllReady(); - - // THEN the display is unconfigured - verify(mUdfpsView).unconfigureDisplay(); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // WHEN up-action received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - mFgExecutor.runAllReady(); - - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void onAcquiredGood_displayConfigurationChange() { - runWithAllParams(this::onAcquiredGood_displayConfigurationParameterized); - } - - private void onAcquiredGood_displayConfigurationParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - - // GIVEN overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - // WHEN ACQUIRED_GOOD received - mOverlayController.onAcquired(testParams.sensorProps.sensorId, - BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - // THEN the display is unconfigured - verify(mUdfpsView).unconfigureDisplay(); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - // WHEN ACQUIRED_GOOD received - mOverlayController.onAcquired(testParams.sensorProps.sensorId, - BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void aodInterruptTimeout() { - runWithAllParams(this::aodInterruptTimeoutParameterized); - } - - private void aodInterruptTimeoutParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView); - - // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - } - // WHEN it times out - mFgExecutor.advanceClockToNext(); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // THEN the display is unconfigured. - verify(mUdfpsView).unconfigureDisplay(); - } else { - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void aodInterruptCancelTimeoutActionOnFingerUp() { - runWithAllParams(this::aodInterruptCancelTimeoutActionOnFingerUpParameterized); - } - - private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - // GIVEN AOD interrupt - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - mFgExecutor.runAllReady(); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // Configure UdfpsView to accept the ACTION_UP event - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - } - - // WHEN ACTION_UP is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - mFgExecutor.runAllReady(); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // Configure UdfpsView to accept the finger up event - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - } - - // WHEN it times out - mFgExecutor.advanceClockToNext(); - mFgExecutor.runAllReady(); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // THEN the display should be unconfigured once. If the timeout action is not - // cancelled, the display would be unconfigured twice which would cause two - // FP attempts. - verify(mUdfpsView).unconfigureDisplay(); - } else { - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void aodInterruptScreenOff() { - runWithAllParams(this::aodInterruptScreenOffParameterized); - } - - private void aodInterruptScreenOffParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView); - - // GIVEN screen off - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOff(); - mFgExecutor.runAllReady(); - - // WHEN aod interrupt is received - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - - // THEN display doesn't get configured because it's off - verify(mUdfpsView, never()).configureDisplay(any()); - } - - @Test - public void aodInterrupt_fingerprintNotRunning() { - runWithAllParams(this::aodInterrupt_fingerprintNotRunningParameterized); - } - - private void aodInterrupt_fingerprintNotRunningParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - - // GIVEN showing overlay - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - - // WHEN aod interrupt is received when the fingerprint service isn't running - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - - // THEN display doesn't get configured because it's off - verify(mUdfpsView, never()).configureDisplay(any()); - } - - @Test - public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true); - - // WHEN ACTION_HOVER is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); - MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0); - mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent); - enterEvent.recycle(); - - // THEN context click haptic is played - verify(mVibrator).performHapticFeedback( - any(), - eq(HapticFeedbackConstants.CONTEXT_CLICK) - ); - } - - @Test - public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN NO haptic played - verify(mVibrator, never()).performHapticFeedback(any(), anyInt()); - } - - @Test - public void fingerDown_falsingManagerInformed() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN falsing manager is informed of the touch - verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION); - } - - private Pair<TouchProcessorResult, TouchProcessorResult> givenFingerEvent( - InteractionEvent event1, InteractionEvent event2, boolean a11y) - throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - event1, 1 /* pointerId */, touchData); - final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch( - event2, 1 /* pointerId */, touchData); - - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - if (a11y) { - verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); - } else { - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - } - - return new Pair<>(processorResultDown, processorResultUp); - } - - @Test - public void onTouch_forwardToKeyguard() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData); - - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - - // THEN the touch is forwarded to Keyguard - verify(mStatusBarKeyguardViewManager).onTouch(downEvent); - } - - @Test - public void onTouch_pilferPointer() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent event = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - - // WHEN ACTION_MOVE is received after - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - event.setAction(ACTION_MOVE); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - - // THEN only pilfer once on the initial down - verify(mInputManager).pilferPointers(any()); - } - - @Test - public void onTouch_doNotPilferPointer() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultUnchanged = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED, - -1 /* pointerId */, touchData); - - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN ACTION_DOWN is received and touch is not within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultUnchanged); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN the touch is NOT pilfered - verify(mInputManager, never()).pilferPointers(any()); - } - - @Test - public void onDownTouchReceivedWithoutPreviousUp() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, - -1 /* pointerId */, touchData); - - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN ACTION_DOWN is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent); - mBiometricExecutor.runAllReady(); - firstDownEvent.recycle(); - - // And another ACTION_DOWN is received without an ACTION_UP before - MotionEvent secondDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, secondDownEvent); - mBiometricExecutor.runAllReady(); - secondDownEvent.recycle(); - - // THEN the touch is still processed - verify(mFingerprintManager, times(2)).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void onAodDownAndDownTouchReceived() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, - -1 /* pointerId */, touchData); - - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN fingerprint is requested because of AOD interrupt - // GIVEN there's been an AoD interrupt - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - mScreenObserver.onScreenTurnedOn(); - mUdfpsController.onAodInterrupt(0, 0, 0, 0); - mFgExecutor.runAllReady(); - - // and an ACTION_DOWN is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent); - mBiometricExecutor.runAllReady(); - firstDownEvent.recycle(); - - // THEN the touch is only processed once - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void onTouch_pilferPointerWhenAltBouncerShowing() - throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false); - - // WHEN alternate bouncer is showing - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // WHEN ACTION_DOWN is received and touch is not within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN the touch is pilfered - verify(mInputManager).pilferPointers(any()); - } - - @Test - public void onTouch_doNotProcessTouchWhenPullingUpBouncer() - throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false); - - // GIVEN a swipe up to bring up primary bouncer is in progress or swipe down for QS - when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true); - when(mLockscreenShadeTransitionController.getFractionToShade()).thenReturn(1f); - - // WHEN ACTION_MOVE is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN the touch is NOT processed - verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - - @Test - public void onTouch_qsDrag_processesTouchWhenAlternateBouncerVisible() - throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - // GIVEN swipe down for QS - when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false); - when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f); - - // WHEN ACTION_MOVE is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN the touch is still processed - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException { - // GIVEN UDFPS overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - // GIVEN there's been an AoD interrupt - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - mScreenObserver.onScreenTurnedOn(); - mUdfpsController.onAodInterrupt(0, 0, 0, 0); - - // THEN finger is considered down - assertTrue(mUdfpsController.isFingerDown()); - - // WHEN udfps receives an ACQUIRED_GOOD after the display is configured - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - verify(mFingerprintManager).setUdfpsOverlayController( - mUdfpsOverlayControllerCaptor.capture()); - mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - - // THEN is fingerDown should be FALSE - assertFalse(mUdfpsController.isFingerDown()); - } - - @Test - public void playHaptic_onAodInterrupt_onAcquiredBad_performHapticFeedback() - throws RemoteException { - // GIVEN UDFPS overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - // GIVEN there's been an AoD interrupt - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); - mScreenObserver.onScreenTurnedOn(); - mUdfpsController.onAodInterrupt(0, 0, 0, 0); - - // THEN vibrate is used - verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS)); - } - - @Test - public void onAcquiredCalbacks() { - runWithAllParams( - this::ultrasonicCallbackOnAcquired); - } - - public void ultrasonicCallbackOnAcquired(TestParams testParams) throws RemoteException{ - if (testParams.sensorProps.sensorType - == FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC) { - reset(mUdfpsView); - - UdfpsController.Callback callbackMock = mock(UdfpsController.Callback.class); - mUdfpsController.addCallback(callbackMock); - - // GIVEN UDFPS overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, - mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mFingerprintManager).setUdfpsOverlayController( - mUdfpsOverlayControllerCaptor.capture()); - mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_START); - mFgExecutor.runAllReady(); - - verify(callbackMock).onFingerDown(); - - mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - - verify(callbackMock).onFingerUp(); - } - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java deleted file mode 100644 index 7986051de3e0..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2020 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.biometrics; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; - -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Flags; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.ActivityTransitionAnimator; -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; -import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shade.ShadeExpansionChangeEvent; -import com.android.systemui.shade.ShadeExpansionStateManager; -import com.android.systemui.shade.domain.interactor.ShadeInteractor; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.SystemUIDialogManager; -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.user.domain.interactor.SelectedUserInteractor; -import com.android.systemui.util.concurrency.DelayableExecutor; - -import org.junit.Before; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase { - // Dependencies - protected @Mock UdfpsKeyguardViewLegacy mView; - protected @Mock Context mResourceContext; - protected @Mock StatusBarStateController mStatusBarStateController; - protected @Mock ShadeExpansionStateManager mShadeExpansionStateManager; - protected @Mock StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - protected @Mock LockscreenShadeTransitionController mLockscreenShadeTransitionController; - protected @Mock DumpManager mDumpManager; - protected @Mock DelayableExecutor mExecutor; - protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; - protected @Mock KeyguardStateController mKeyguardStateController; - protected @Mock KeyguardViewMediator mKeyguardViewMediator; - protected @Mock ConfigurationController mConfigurationController; - protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; - protected @Mock SystemUIDialogManager mDialogManager; - protected @Mock UdfpsController mUdfpsController; - protected @Mock ActivityTransitionAnimator mActivityTransitionAnimator; - protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; - protected @Mock ShadeInteractor mShadeInteractor; - protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor; - protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate; - protected @Mock SelectedUserInteractor mSelectedUserInteractor; - protected @Mock KeyguardTransitionInteractor mKeyguardTransitionInteractor; - protected @Mock UdfpsOverlayInteractor mUdfpsOverlayInteractor; - - protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); - - protected UdfpsKeyguardViewControllerLegacy mController; - - // Capture listeners so that they can be used to send events - private @Captor ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor; - protected StatusBarStateController.StateListener mStatusBarStateListener; - - private @Captor ArgumentCaptor<KeyguardStateController.Callback> - mKeyguardStateControllerCallbackCaptor; - protected KeyguardStateController.Callback mKeyguardStateControllerCallback; - - private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.KeyguardViewManagerCallback> - mKeyguardViewManagerCallbackArgumentCaptor; - protected StatusBarKeyguardViewManager.KeyguardViewManagerCallback mKeyguardViewManagerCallback; - - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); - when(mView.getContext()).thenReturn(mResourceContext); - when(mResourceContext.getString(anyInt())).thenReturn("test string"); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false); - when(mView.getUnpausedAlpha()).thenReturn(255); - when(mShadeExpansionStateManager.addExpansionListener(any())).thenReturn( - new ShadeExpansionChangeEvent(0, false, false)); - mController = createUdfpsKeyguardViewController(); - } - - protected void sendStatusBarStateChanged(int statusBarState) { - mStatusBarStateListener.onStateChanged(statusBarState); - } - - protected void captureStatusBarStateListeners() { - verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture()); - mStatusBarStateListener = mStateListenerCaptor.getValue(); - } - - protected void captureKeyguardStateControllerCallback() { - verify(mKeyguardStateController).addCallback( - mKeyguardStateControllerCallbackCaptor.capture()); - mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue(); - } - - public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() { - return createUdfpsKeyguardViewController(false); - } - - public void captureKeyGuardViewManagerCallback() { - verify(mStatusBarKeyguardViewManager).addCallback( - mKeyguardViewManagerCallbackArgumentCaptor.capture()); - mKeyguardViewManagerCallback = mKeyguardViewManagerCallbackArgumentCaptor.getValue(); - } - - protected UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController( - boolean useModernBouncer) { - UdfpsKeyguardViewControllerLegacy controller = new UdfpsKeyguardViewControllerLegacy( - mView, - mStatusBarStateController, - mStatusBarKeyguardViewManager, - mKeyguardUpdateMonitor, - mDumpManager, - mLockscreenShadeTransitionController, - mConfigurationController, - mKeyguardStateController, - mUnlockedScreenOffAnimationController, - mDialogManager, - mUdfpsController, - mActivityTransitionAnimator, - mPrimaryBouncerInteractor, - mAlternateBouncerInteractor, - mUdfpsKeyguardAccessibilityDelegate, - mSelectedUserInteractor, - mKeyguardTransitionInteractor, - mShadeInteractor, - mUdfpsOverlayInteractor); - return controller; - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java deleted file mode 100644 index 98d8b054716c..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2020 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.biometrics; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.testing.TestableLooper; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.systemui.statusbar.StatusBarState; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) - -@TestableLooper.RunWithLooper(setAsMainLooper = true) -public class UdfpsKeyguardViewLegacyControllerTest extends - UdfpsKeyguardViewLegacyControllerBaseTest { - @Override - public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() { - return createUdfpsKeyguardViewController(/* useModernBouncer */ false); - } - - @Test - public void testShouldPauseAuth_bouncerShowing() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true); - when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true); - when(mView.getUnpausedAlpha()).thenReturn(0); - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testRegistersStatusBarStateListenersOnAttached() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - } - - @Test - public void testViewControllerQueriesSBStateOnAttached() { - mController.onViewAttached(); - verify(mStatusBarStateController).getState(); - - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); - captureStatusBarStateListeners(); - - mController.onViewAttached(); - verify(mView, atLeast(1)).setPauseAuth(true); - } - - @Test - public void testListenersUnregisteredOnDetached() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureKeyguardStateControllerCallback(); - mController.onViewDetached(); - - verify(mStatusBarStateController).removeCallback(mStatusBarStateListener); - verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback); - } - - @Test - public void testShouldPauseAuthUnpausedAlpha0() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - when(mView.getUnpausedAlpha()).thenReturn(0); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldNotPauseAuthOnKeyguard() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - assertFalse(mController.shouldPauseAuth()); - } - - @Test - public void onBiometricAuthenticated_pauseAuth() { - // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard) - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - // WHEN biometric is authenticated - captureKeyguardStateControllerCallback(); - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardStateControllerCallback.onUnlockedChanged(); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthIsLaunchTransitionFadingAway() { - // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard) - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - // WHEN isLaunchTransitionFadingAway=true - captureKeyguardStateControllerCallback(); - when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true); - mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged(); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthOnShadeLocked() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); - - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthOnShade() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN not on keyguard yet (shade = home) - sendStatusBarStateChanged(StatusBarState.SHADE); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthAnimatingScreenOffFromShade() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN transitioning from home/shade => keyguard + animating screen off - mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testDoNotPauseAuthAnimatingScreenOffFromLS() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN animating screen off transition from LS => AOD - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); - - // THEN don't pause auth - assertFalse(mController.shouldPauseAuth()); - } - - @Test - public void testOverrideShouldPauseAuthOnShadeLocked() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); - assertTrue(mController.shouldPauseAuth()); - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt deleted file mode 100644 index 29a6e56891af..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ /dev/null @@ -1,643 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.testing.TestableLooper -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF -import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN -import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor -import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository -import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor -import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor -import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.keyguard.shared.model.TransitionState -import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.kosmos.testScope -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.whenever -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mockito -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@RunWith(AndroidJUnit4::class) -@SmallTest -@TestableLooper.RunWithLooper -@kotlinx.coroutines.ExperimentalCoroutinesApi -class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : - UdfpsKeyguardViewLegacyControllerBaseTest() { - private val kosmos = testKosmos() - private val testScope = kosmos.testScope - - private val keyguardBouncerRepository = kosmos.fakeKeyguardBouncerRepository - private val transitionRepository = kosmos.fakeKeyguardTransitionRepository - - @Before - override fun setUp() { - allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread - MockitoAnnotations.initMocks(this) - super.setUp() - } - - override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewControllerLegacy { - mPrimaryBouncerInteractor = kosmos.primaryBouncerInteractor - mAlternateBouncerInteractor = kosmos.alternateBouncerInteractor - mKeyguardTransitionInteractor = kosmos.keyguardTransitionInteractor - mUdfpsOverlayInteractor = kosmos.udfpsOverlayInteractor - return createUdfpsKeyguardViewController(/* useModernBouncer */ true) - } - - @Test - fun bouncerExpansionChange_fadeIn() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - captureKeyguardStateControllerCallback() - Mockito.reset(mView) - - // WHEN status bar expansion is 0 - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN alpha is 0 - verify(mView).unpausedAlpha = 0 - - job.cancel() - } - - @Test - fun bouncerExpansionChange_pauseAuth() = - testScope.runTest { - // GIVEN view is attached + on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - Mockito.reset(mView) - - // WHEN panelViewExpansion changes to hide - whenever(mView.unpausedAlpha).thenReturn(0) - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN pause auth is updated to PAUSE - verify(mView, Mockito.atLeastOnce()).setPauseAuth(true) - - job.cancel() - } - - @Test - fun bouncerExpansionChange_unpauseAuth() = - testScope.runTest { - // GIVEN view is attached + on the keyguard + panel expansion is 0f - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - Mockito.reset(mView) - - // WHEN panelViewExpansion changes to expanded - whenever(mView.unpausedAlpha).thenReturn(255) - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN) - runCurrent() - - // THEN pause auth is updated to NOT pause - verify(mView, Mockito.atLeastOnce()).setPauseAuth(false) - - job.cancel() - } - - @Test - fun shadeLocked_showAlternateBouncer_unpauseAuth() = - testScope.runTest { - // GIVEN view is attached + on the SHADE_LOCKED (udfps view not showing) - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED) - - // WHEN alternate bouncer is requested - val job = mController.listenForAlternateBouncerVisibility(this) - keyguardBouncerRepository.setAlternateVisible(true) - runCurrent() - - // THEN udfps view will animate in & pause auth is updated to NOT pause - verify(mView).animateInUdfpsBouncer(any()) - assertFalse(mController.shouldPauseAuth()) - - job.cancel() - } - - /** After migration to MODERN_BOUNCER, replaces UdfpsKeyguardViewControllerTest version */ - @Test - fun shouldPauseAuthBouncerShowing() = - testScope.runTest { - // GIVEN view attached and we're on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - - // WHEN the bouncer expansion is VISIBLE - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN UDFPS shouldPauseAuth == true - assertTrue(mController.shouldPauseAuth()) - - job.cancel() - } - - @Test - fun shouldHandleTouchesChange() = - testScope.runTest { - val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches) - - // GIVEN view is attached + on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - whenever(mView.setPauseAuth(true)).thenReturn(true) - whenever(mView.unpausedAlpha).thenReturn(0) - - // WHEN panelViewExpansion changes to expanded - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN UDFPS auth is paused and should not handle touches - assertThat(mController.shouldPauseAuth()).isTrue() - assertThat(shouldHandleTouches!!).isFalse() - - job.cancel() - } - - @Test - fun shouldHandleTouchesOnDetach() = - testScope.runTest { - val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches) - - // GIVEN view is attached + on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - whenever(mView.setPauseAuth(true)).thenReturn(true) - whenever(mView.unpausedAlpha).thenReturn(0) - - // WHEN panelViewExpansion changes to expanded - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - mController.onViewDetached() - - // THEN UDFPS auth is paused and should not handle touches - assertThat(mController.shouldPauseAuth()).isTrue() - assertThat(shouldHandleTouches!!).isFalse() - - job.cancel() - } - - @Test - fun fadeFromDialogSuggestedAlpha() = - testScope.runTest { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached() - captureStatusBarStateListeners() - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN) - runCurrent() - Mockito.reset(mView) - - // WHEN dialog suggested alpha is .6f - whenever(mView.dialogSuggestedAlpha).thenReturn(.6f) - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - - // THEN alpha is updated based on dialog suggested alpha - verify(mView).unpausedAlpha = (.6f * 255).toInt() - - job.cancel() - } - - @Test - fun transitionToFullShadeProgress() = - testScope.runTest { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached() - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN) - runCurrent() - Mockito.reset(mView) - whenever(mView.dialogSuggestedAlpha).thenReturn(1f) - - // WHEN we're transitioning to the full shade - val transitionProgress = .6f - mController.setTransitionToFullShadeProgress(transitionProgress) - - // THEN alpha is between 0 and 255 - verify(mView).unpausedAlpha = ((1f - transitionProgress) * 255).toInt() - - job.cancel() - } - - @Test - fun aodToLockscreen_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForLockscreenAodTransitions(this) - - // WHEN transitioning from lockscreen to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - job.cancel() - } - - @Test - fun lockscreenToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForLockscreenAodTransitions(this) - - // WHEN transitioning from lockscreen to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - job.cancel() - } - - @Test - fun goneToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForGoneToAodTransition(this) - - // WHEN transitioning from lockscreen to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - job.cancel() - } - - @Test - fun aodToOccluded_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForAodToOccludedTransitions(this) - - // WHEN transitioning from aod to occluded - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.AOD, - to = KeyguardState.OCCLUDED, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged(eq(.7f), eq(.7f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE)) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.AOD, - to = KeyguardState.OCCLUDED, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE)) - - job.cancel() - } - - @Test - fun occludedToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForOccludedToAodTransition(this) - - // WHEN transitioning from occluded to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.OCCLUDED, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.OCCLUDED, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - job.cancel() - } - - @Test - fun cancelledAodToLockscreen_dozeAmountChangedToZero() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - val job = mController.listenForLockscreenAodTransitions(this) - runCurrent() - Mockito.reset(mView) - - // WHEN aod to lockscreen transition is cancelled - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.AOD, - to = KeyguardState.LOCKSCREEN, - value = 1f, - transitionState = TransitionState.CANCELED - ) - ) - runCurrent() - // ... and WHEN the next transition is from lockscreen => occluded - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, - value = .4f, - transitionState = TransitionState.STARTED - ) - ) - runCurrent() - - // THEN doze amount is updated to zero - verify(mView) - .onDozeAmountChanged(eq(0f), eq(0f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)) - job.cancel() - } - - @Test - fun cancelledLockscreenToAod_dozeAmountNotUpdatedToZero() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - val job = mController.listenForLockscreenAodTransitions(this) - runCurrent() - Mockito.reset(mView) - - // WHEN lockscreen to aod transition is cancelled - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.CANCELED - ) - ) - runCurrent() - - // THEN doze amount is NOT updated to zero - verify(mView, never()).onDozeAmountChanged(eq(0f), eq(0f), anyInt()) - job.cancel() - } - - @Test - fun dreamingToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForDreamingToAodTransitions(this) - // WHEN dreaming to aod transition in progress - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.DREAMING, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - - // THEN doze amount is updated to - verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF)) - job.cancel() - } - - @Test - fun alternateBouncerToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForAlternateBouncerToAodTransitions(this) - // WHEN alternate bouncer to aod transition in progress - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - - // THEN doze amount is updated to - verify(mView) - .onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)) - job.cancel() - } - - @Test - fun bouncerToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForPrimaryBouncerToAodTransitions(this) - // WHEN alternate bouncer to aod transition in progress - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - - // THEN doze amount is updated to - verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF)) - job.cancel() - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt index 68cfa28dabd7..82ff61795e98 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt @@ -16,12 +16,9 @@ package com.android.systemui.bouncer.domain.interactor -import android.platform.test.annotations.DisableFlags -import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.keyguardUpdateMonitor -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.domain.interactor.authenticationInteractor @@ -61,12 +58,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { underTest = kosmos.alternateBouncerInteractor } - @Test(expected = IllegalStateException::class) - @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun enableUdfpsRefactor_deprecatedShowMethod_throwsIllegalStateException() { - underTest.show() - } - @Test @DisableSceneContainer fun canShowAlternateBouncer_false_dueToTransitionState() = @@ -101,21 +92,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun canShowAlternateBouncerForFingerprint_givenCanShow() { - givenCanShowAlternateBouncer() - assertTrue(underTest.canShowAlternateBouncerForFingerprint()) - } - - @Test - fun canShowAlternateBouncerForFingerprint_alternateBouncerUIUnavailable() { - givenCanShowAlternateBouncer() - kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(false) - - assertFalse(underTest.canShowAlternateBouncerForFingerprint()) - } - - @Test fun canShowAlternateBouncerForFingerprint_ifFingerprintIsNotUsuallyAllowed() { givenCanShowAlternateBouncer() kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) @@ -140,15 +116,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun show_whenCanShow() { - givenCanShowAlternateBouncer() - - assertTrue(underTest.show()) - assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value) - } - - @Test fun canShowAlternateBouncerForFingerprint_butCanDismissLockScreen() { givenCanShowAlternateBouncer() whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true) @@ -165,15 +132,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun show_whenCannotShow() { - givenCannotShowAlternateBouncer() - - assertFalse(underTest.show()) - assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value) - } - - @Test fun hide_wasPreviouslyShowing() { kosmos.keyguardBouncerRepository.setAlternateVisible(true) @@ -190,7 +148,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun canShowAlternateBouncerForFingerprint_rearFps() { givenCanShowAlternateBouncer() kosmos.fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer @@ -198,29 +155,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { assertFalse(underTest.canShowAlternateBouncerForFingerprint()) } - @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun alternateBouncerUiAvailable_fromMultipleSources() { - assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - - // GIVEN there are two different sources indicating the alternate bouncer is available - underTest.setAlternateBouncerUIAvailable(true, "source1") - underTest.setAlternateBouncerUIAvailable(true, "source2") - assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - - // WHEN one of the sources no longer says the UI is available - underTest.setAlternateBouncerUIAvailable(false, "source1") - - // THEN alternate bouncer UI is still available (from the other source) - assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - - // WHEN all sources say the UI is not available - underTest.setAlternateBouncerUIAvailable(false, "source2") - - // THEN alternate boucer UI is not available - assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - } - private fun givenAlternateBouncerSupported() { kosmos.givenAlternateBouncerSupported() } @@ -228,8 +162,4 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { private fun givenCanShowAlternateBouncer() { kosmos.givenCanShowAlternateBouncer() } - - private fun givenCannotShowAlternateBouncer() { - kosmos.givenCannotShowAlternateBouncer() - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt index ee65fbd810ae..1e8651683ec1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.communal +import android.os.UserHandle import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings @@ -77,7 +78,11 @@ class CommunalSceneStartableTest : SysuiTestCase() { @Before fun setUp() { with(kosmos) { - fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT) + fakeSettings.putIntForUser( + Settings.System.SCREEN_OFF_TIMEOUT, + SCREEN_TIMEOUT, + UserHandle.USER_CURRENT, + ) kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) underTest = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt index 3f5b9a35d3a5..bec8a30320e7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt @@ -16,6 +16,14 @@ package com.android.systemui.display.data.repository +import android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY +import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT +import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN import android.hardware.devicestate.DeviceStateManager import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -40,6 +48,8 @@ import org.junit.Before import org.junit.runner.RunWith import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.kotlin.whenever +import android.hardware.devicestate.DeviceState as PlatformDeviceState @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @@ -59,15 +69,33 @@ class DeviceStateRepositoryTest : SysuiTestCase() { @Before fun setup() { mContext.orCreateTestableResources.apply { - addOverride(R.array.config_foldedDeviceStates, listOf(TEST_FOLDED).toIntArray()) - addOverride(R.array.config_halfFoldedDeviceStates, TEST_HALF_FOLDED.toIntArray()) - addOverride(R.array.config_openDeviceStates, TEST_UNFOLDED.toIntArray()) - addOverride(R.array.config_rearDisplayDeviceStates, TEST_REAR_DISPLAY.toIntArray()) + addOverride( + R.array.config_foldedDeviceStates, + listOf(TEST_FOLDED.identifier).toIntArray() + ) + addOverride( + R.array.config_halfFoldedDeviceStates, + TEST_HALF_FOLDED.identifier.toIntArray() + ) + addOverride(R.array.config_openDeviceStates, TEST_UNFOLDED.identifier.toIntArray()) + addOverride( + R.array.config_rearDisplayDeviceStates, + TEST_REAR_DISPLAY.identifier.toIntArray() + ) addOverride( R.array.config_concurrentDisplayDeviceStates, - TEST_CONCURRENT_DISPLAY.toIntArray() + TEST_CONCURRENT_DISPLAY.identifier.toIntArray() ) } + whenever(deviceStateManager.supportedDeviceStates).thenReturn( + listOf( + TEST_FOLDED, + TEST_HALF_FOLDED, + TEST_UNFOLDED, + TEST_REAR_DISPLAY, + TEST_CONCURRENT_DISPLAY + ) + ) deviceStateRepository = DeviceStateRepositoryImpl( mContext, @@ -85,9 +113,7 @@ class DeviceStateRepositoryTest : SysuiTestCase() { testScope.runTest { val state = displayState() - deviceStateManagerListener.value.onDeviceStateChanged( - getDeviceStateForIdentifier(TEST_FOLDED) - ) + deviceStateManagerListener.value.onDeviceStateChanged(TEST_FOLDED) assertThat(state()).isEqualTo(DeviceState.FOLDED) } @@ -97,9 +123,7 @@ class DeviceStateRepositoryTest : SysuiTestCase() { testScope.runTest { val state = displayState() - deviceStateManagerListener.value.onDeviceStateChanged( - getDeviceStateForIdentifier(TEST_HALF_FOLDED) - ) + deviceStateManagerListener.value.onDeviceStateChanged(TEST_HALF_FOLDED) assertThat(state()).isEqualTo(DeviceState.HALF_FOLDED) } @@ -109,9 +133,7 @@ class DeviceStateRepositoryTest : SysuiTestCase() { testScope.runTest { val state = displayState() - deviceStateManagerListener.value.onDeviceStateChanged( - getDeviceStateForIdentifier(TEST_UNFOLDED) - ) + deviceStateManagerListener.value.onDeviceStateChanged(TEST_UNFOLDED) assertThat(state()).isEqualTo(DeviceState.UNFOLDED) } @@ -121,9 +143,7 @@ class DeviceStateRepositoryTest : SysuiTestCase() { testScope.runTest { val state = displayState() - deviceStateManagerListener.value.onDeviceStateChanged( - getDeviceStateForIdentifier(TEST_REAR_DISPLAY) - ) + deviceStateManagerListener.value.onDeviceStateChanged(TEST_REAR_DISPLAY) assertThat(state()).isEqualTo(DeviceState.REAR_DISPLAY) } @@ -133,9 +153,7 @@ class DeviceStateRepositoryTest : SysuiTestCase() { testScope.runTest { val state = displayState() - deviceStateManagerListener.value.onDeviceStateChanged( - getDeviceStateForIdentifier(TEST_CONCURRENT_DISPLAY) - ) + deviceStateManagerListener.value.onDeviceStateChanged(TEST_CONCURRENT_DISPLAY) assertThat(state()).isEqualTo(DeviceState.CONCURRENT_DISPLAY) } @@ -164,9 +182,9 @@ class DeviceStateRepositoryTest : SysuiTestCase() { private fun Int.toIntArray() = listOf(this).toIntArray() - private fun getDeviceStateForIdentifier(id: Int): android.hardware.devicestate.DeviceState { - return android.hardware.devicestate.DeviceState( - android.hardware.devicestate.DeviceState.Configuration.Builder(id, /* name= */ "") + private fun getDeviceStateForIdentifier(id: Int): PlatformDeviceState { + return PlatformDeviceState( + PlatformDeviceState.Configuration.Builder(id, /* name= */ "") .build() ) } @@ -174,10 +192,68 @@ class DeviceStateRepositoryTest : SysuiTestCase() { private companion object { // Used to fake the ids in the test. Note that there is no guarantees different devices will // have the same ids (that's why the ones in this test start from 41) - const val TEST_FOLDED = 41 - const val TEST_HALF_FOLDED = 42 - const val TEST_UNFOLDED = 43 - const val TEST_REAR_DISPLAY = 44 - const val TEST_CONCURRENT_DISPLAY = 45 + val TEST_FOLDED = + PlatformDeviceState( + PlatformDeviceState.Configuration.Builder(41, "FOLDED") + .setSystemProperties( + setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED) + ) + .build() + ) + val TEST_HALF_FOLDED = + PlatformDeviceState( + PlatformDeviceState.Configuration.Builder(42, "HALF_FOLDED") + .setSystemProperties( + setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN) + ) + .build() + ) + val TEST_UNFOLDED = + PlatformDeviceState( + PlatformDeviceState.Configuration.Builder(43, "UNFOLDED") + .setSystemProperties( + setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN) + ) + .build() + ) + val TEST_REAR_DISPLAY = + PlatformDeviceState( + PlatformDeviceState.Configuration.Builder(44, "REAR_DISPLAY") + .setSystemProperties( + setOf( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY, + PROPERTY_FEATURE_REAR_DISPLAY, + PROPERTY_EMULATED_ONLY + ) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN) + ) + .build() + ) + val TEST_CONCURRENT_DISPLAY = + PlatformDeviceState( + PlatformDeviceState.Configuration.Builder(45, "CONCURRENT_DISPLAY") + .setSystemProperties( + setOf( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY, + PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT, + PROPERTY_EMULATED_ONLY + ) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN) + ) + .build() + ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt index faaa4c415d28..1dd8ca9221a4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt @@ -14,23 +14,15 @@ * limitations under the License. */ -package com.android.systemui.statusbar.window +package com.android.systemui.display.data.repository -import android.platform.test.annotations.EnableFlags import android.view.Display -import android.view.WindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCaptureAwareWindowManager -import com.android.app.viewcapture.mockViewCaptureAwareWindowManager import com.android.systemui.SysuiTestCase -import com.android.systemui.display.data.repository.displayRepository -import com.android.systemui.display.data.repository.fakeDisplayWindowPropertiesRepository -import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.unconfinedTestDispatcher -import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.runBlocking @@ -43,28 +35,13 @@ import org.mockito.kotlin.mock @RunWith(AndroidJUnit4::class) @SmallTest -@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) -class MultiDisplayStatusBarWindowControllerStoreTest : SysuiTestCase() { +class PerDisplayStoreImplTest : SysuiTestCase() { private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher } private val testScope = kosmos.testScope private val fakeDisplayRepository = kosmos.displayRepository - private val store = - MultiDisplayStatusBarWindowControllerStore( - backgroundApplicationScope = kosmos.applicationCoroutineScope, - controllerFactory = kosmos.fakeStatusBarWindowControllerFactory, - displayWindowPropertiesRepository = kosmos.fakeDisplayWindowPropertiesRepository, - viewCaptureAwareWindowManagerFactory = - object : ViewCaptureAwareWindowManager.Factory { - override fun create( - windowManager: WindowManager - ): ViewCaptureAwareWindowManager { - return kosmos.mockViewCaptureAwareWindowManager - } - }, - displayRepository = fakeDisplayRepository, - ) + private val store = kosmos.fakePerDisplayStore @Before fun start() { @@ -80,34 +57,52 @@ class MultiDisplayStatusBarWindowControllerStoreTest : SysuiTestCase() { @Test fun forDisplay_defaultDisplay_multipleCalls_returnsSameInstance() = testScope.runTest { - val controller = store.defaultDisplay + val instance = store.defaultDisplay - assertThat(store.defaultDisplay).isSameInstanceAs(controller) + assertThat(store.defaultDisplay).isSameInstanceAs(instance) } @Test fun forDisplay_nonDefaultDisplay_multipleCalls_returnsSameInstance() = testScope.runTest { - val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID) + val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID) - assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(controller) + assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(instance) } @Test fun forDisplay_nonDefaultDisplay_afterDisplayRemoved_returnsNewInstance() = testScope.runTest { - val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID) + val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID) fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID) fakeDisplayRepository.addDisplay(createDisplay(NON_DEFAULT_DISPLAY_ID)) - assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(controller) + assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(instance) } @Test(expected = IllegalArgumentException::class) fun forDisplay_nonExistingDisplayId_throws() = testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) } + @Test + fun forDisplay_afterDisplayRemoved_onDisplayRemovalActionInvoked() = + testScope.runTest { + val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID) + + fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID) + + assertThat(store.removalActions).containsExactly(instance) + } + + @Test + fun forDisplay_withoutDisplayRemoval_onDisplayRemovalActionIsNotInvoked() = + testScope.runTest { + store.forDisplay(NON_DEFAULT_DISPLAY_ID) + + assertThat(store.removalActions).isEmpty() + } + private fun createDisplay(displayId: Int): Display = mock { on { getDisplayId() } doReturn displayId } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java index fad52e090c69..ba43a236b8f9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java @@ -38,9 +38,9 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt new file mode 100644 index 000000000000..90727b240caf --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.education.domain.ui.view + +import android.testing.TestableLooper +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityManager +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.activity.EmptyTestActivity +import com.android.systemui.education.ui.view.ContextualEduDialog +import com.android.systemui.education.ui.viewmodel.ContextualEduToastViewModel +import kotlin.test.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.firstValue +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper +class ContextualEduDialogTest : SysuiTestCase() { + @Rule + @JvmField + val activityRule: ActivityScenarioRule<EmptyTestActivity> = + ActivityScenarioRule(EmptyTestActivity::class.java) + @get:Rule val mockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var accessibilityManager: AccessibilityManager + private lateinit var underTest: ContextualEduDialog + + @Before + fun setUp() { + whenever(accessibilityManager.isEnabled).thenReturn(true) + } + + @Test + fun sendAccessibilityInfo() { + val message = "Testing message" + val viewModel = ContextualEduToastViewModel(message, icon = 0, userId = 0) + activityRule.scenario.onActivity { + underTest = ContextualEduDialog(context, viewModel, accessibilityManager) + underTest.show() + } + + val eventCaptor = ArgumentCaptor.forClass(AccessibilityEvent::class.java) + verify(accessibilityManager).sendAccessibilityEvent(eventCaptor.capture()) + assertEquals(message, eventCaptor.firstValue.text[0]) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt index e251ab50e3c7..baf3b5b4430f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt @@ -20,7 +20,10 @@ import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardViewController import com.android.systemui.Flags import com.android.systemui.SysuiTestCase +import com.android.systemui.defaultDeviceState +import com.android.systemui.deviceStateManager import com.android.systemui.flags.FeatureFlags +import com.android.systemui.kosmos.Kosmos import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.SysuiStatusBarStateController @@ -28,7 +31,6 @@ import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argThat -import com.android.systemui.util.mockito.whenever import java.util.function.Predicate import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse @@ -47,6 +49,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations import org.mockito.kotlin.clearInvocations +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @RunWithLooper @@ -65,6 +68,8 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController @Mock private lateinit var powerManager: PowerManager @Mock private lateinit var wallpaperManager: WallpaperManager + private val kosmos = Kosmos() + private val deviceStateManager = kosmos.deviceStateManager @Mock private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub @@ -173,7 +178,8 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { statusBarStateController, notificationShadeWindowController, powerManager, - wallpaperManager + wallpaperManager, + deviceStateManager ) { override fun shouldPerformSmartspaceTransition(): Boolean = shouldPerformSmartspaceTransition @@ -185,6 +191,8 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java)) whenever(powerManager.isInteractive).thenReturn(true) + whenever(deviceStateManager.supportedDeviceStates) + .thenReturn(listOf(kosmos.defaultDeviceState)) // All of these fields are final, so we can't mock them, but are needed so that the surface // appear amount setter doesn't short circuit. diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt index 2a2a82d53671..b5ea305544ff 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt @@ -39,7 +39,6 @@ import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsReposi import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.kosmos.testScope -import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes @@ -100,18 +99,14 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { FakeTrustRepository(), testScope.backgroundScope, mSelectedUserInteractor, - faceAuthInteractor + faceAuthInteractor, ) alternateBouncerInteractor = AlternateBouncerInteractor( - mock(StatusBarStateController::class.java), - mock(KeyguardStateController::class.java), bouncerRepository, FakeFingerprintPropertyRepository(), - biometricSettingsRepository, FakeSystemClock(), - keyguardUpdateMonitor, { mock(DeviceEntryBiometricsAllowedInteractor::class.java) }, { mock(KeyguardInteractor::class.java) }, { mock(KeyguardTransitionInteractor::class.java) }, @@ -121,13 +116,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { underTest = DeviceEntrySideFpsOverlayInteractor( - testScope.backgroundScope, mContext, deviceEntryFingerprintAuthRepository, kosmos.sceneInteractor, primaryBouncerInteractor, alternateBouncerInteractor, - keyguardUpdateMonitor + keyguardUpdateMonitor, ) } @@ -142,7 +136,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isTrue() } @@ -158,7 +152,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = false, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -169,13 +163,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { testScope.runTest { underTest = DeviceEntrySideFpsOverlayInteractor( - testScope.backgroundScope, mContext, deviceEntryFingerprintAuthRepository, kosmos.sceneInteractor, primaryBouncerInteractor, alternateBouncerInteractor, - keyguardUpdateMonitor + keyguardUpdateMonitor, ) val showIndicatorForDeviceEntry by @@ -185,7 +178,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = true, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isTrue() } @@ -196,13 +189,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { testScope.runTest { underTest = DeviceEntrySideFpsOverlayInteractor( - testScope.backgroundScope, mContext, deviceEntryFingerprintAuthRepository, kosmos.sceneInteractor, primaryBouncerInteractor, alternateBouncerInteractor, - keyguardUpdateMonitor + keyguardUpdateMonitor, ) val showIndicatorForDeviceEntry by @@ -212,7 +204,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -228,7 +220,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = false, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -245,7 +237,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = false + isUnlockingWithFpAllowed = false, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -261,7 +253,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = true, fpsDetectionRunning = false, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -277,7 +269,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = true, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = false + isUnlockingWithFpAllowed = false, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -294,7 +286,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = true, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -325,7 +317,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) // Another request to show indicator for deviceEntryFingerprintAuthRepository update @@ -355,7 +347,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { .thenReturn(isUnlockingWithFpAllowed) mContext.orCreateTestableResources.addOverride( R.bool.config_show_sidefps_hint_on_bouncer, - true + true, ) } @@ -366,7 +358,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { ) { kosmos.sceneInteractor.changeScene( if (isActive) Scenes.Bouncer else Scenes.Lockscreen, - "reason" + "reason", ) whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning) @@ -375,7 +367,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { .thenReturn(isUnlockingWithFpAllowed) mContext.orCreateTestableResources.addOverride( R.bool.config_show_sidefps_hint_on_bouncer, - true + true, ) runCurrent() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index 3fb3eead6469..b3417b9de36d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -150,6 +150,53 @@ class KeyguardInteractorTest : SysuiTestCase() { assertThat(secureCameraActive()).isFalse() } + /** Regression test for b/373700726. */ + @Test + @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testSecureCameraStillFalseAfterDeviceUnlocked() = + testScope.runTest { + val secureCameraActive = collectLastValue(underTest.isSecureCameraActive) + runCurrent() + + // Launch camera + underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) + assertThat(secureCameraActive()).isTrue() + + // Go back to keyguard + repository.setKeyguardShowing(true) + repository.setKeyguardOccluded(false) + assertThat(secureCameraActive()).isFalse() + + // WHEN device is unlocked (and therefore keyguard is no longer showing) + repository.setKeyguardShowing(false) + + // THEN we still show secure camera as *not* active + assertThat(secureCameraActive()).isFalse() + } + + /** Regression test for b/373700726. */ + @Test + @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testSecureCameraStillFalseAfterBouncerDismissed() = + testScope.runTest { + val secureCameraActive = collectLastValue(underTest.isSecureCameraActive) + runCurrent() + + // Launch camera + underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) + assertThat(secureCameraActive()).isTrue() + + // Show bouncer + bouncerRepository.setPrimaryShow(true) + assertThat(secureCameraActive()).isFalse() + + // WHEN device is unlocked (and therefore the bouncer is no longer showing) + bouncerRepository.setPrimaryShow(false) + + // THEN we still show secure camera as *not* active + assertThat(secureCameraActive()).isFalse() + } + @Test @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest { @@ -182,11 +229,7 @@ class KeyguardInteractorTest : SysuiTestCase() { val dismissAlpha by collectLastValue(underTest.dismissAlpha) assertThat(dismissAlpha).isEqualTo(1f) - keyguardTransitionRepository.sendTransitionSteps( - from = AOD, - to = LOCKSCREEN, - testScope, - ) + keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope) repository.setStatusBarState(StatusBarState.KEYGUARD) // User begins to swipe up @@ -208,11 +251,7 @@ class KeyguardInteractorTest : SysuiTestCase() { assertThat(dismissAlpha[0]).isEqualTo(1f) assertThat(dismissAlpha.size).isEqualTo(1) - keyguardTransitionRepository.sendTransitionSteps( - from = AOD, - to = LOCKSCREEN, - testScope, - ) + keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope) // User begins to swipe up repository.setStatusBarState(StatusBarState.KEYGUARD) @@ -328,11 +367,7 @@ class KeyguardInteractorTest : SysuiTestCase() { shadeRepository.setLegacyShadeExpansion(0f) - keyguardTransitionRepository.sendTransitionSteps( - from = AOD, - to = LOCKSCREEN, - testScope, - ) + keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope) assertThat(keyguardTranslationY).isEqualTo(0f) } @@ -350,11 +385,7 @@ class KeyguardInteractorTest : SysuiTestCase() { shadeRepository.setLegacyShadeExpansion(1f) - keyguardTransitionRepository.sendTransitionSteps( - from = AOD, - to = LOCKSCREEN, - testScope, - ) + keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope) assertThat(keyguardTranslationY).isEqualTo(0f) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 8f3d54961483..f4d2ea018a44 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -118,9 +118,6 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest private val fromPrimaryBouncerTransitionInteractor by lazy { kosmos.fromPrimaryBouncerTransitionInteractor } - private val fromDreamingLockscreenHostedTransitionInteractor by lazy { - kosmos.fromDreamingLockscreenHostedTransitionInteractor - } private val fromGlanceableHubTransitionInteractor by lazy { kosmos.fromGlanceableHubTransitionInteractor } @@ -161,7 +158,6 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest fromLockscreenTransitionInteractor.start() fromPrimaryBouncerTransitionInteractor.start() fromDreamingTransitionInteractor.start() - fromDreamingLockscreenHostedTransitionInteractor.start() fromAodTransitionInteractor.start() fromGoneTransitionInteractor.start() fromDozingTransitionInteractor.start() @@ -275,36 +271,6 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest } @Test - @BrokenWithSceneContainer(339465026) - fun lockscreenToDreamingLockscreenHosted() = - testScope.runTest { - // GIVEN a device that is not dreaming or dozing - keyguardRepository.setDreamingWithOverlay(false) - keyguardRepository.setDozeTransitionModel( - DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH) - ) - advanceTimeBy(600L) - - // GIVEN a prior transition has run to LOCKSCREEN - runTransitionAndSetWakefulness(KeyguardState.GONE, KeyguardState.LOCKSCREEN) - - // WHEN the device begins to dream and the dream is lockscreen hosted - keyguardRepository.setDreamingWithOverlay(true) - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - advanceTimeBy(100L) - - assertThat(transitionRepository) - .startedTransition( - to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - from = KeyguardState.LOCKSCREEN, - ownerName = "FromLockscreenTransitionInteractor", - animatorAssertion = { it.isNotNull() }, - ) - - coroutineContext.cancelChildren() - } - - @Test fun lockscreenToDozing() = testScope.runTest { // GIVEN a device with AOD not available @@ -355,157 +321,6 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest } @Test - @DisableSceneContainer - fun dreamingLockscreenHostedToLockscreen() = - testScope.runTest { - // GIVEN a device dreaming with the lockscreen hosted dream and not dozing - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - keyguardRepository.setDozeTransitionModel( - DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH) - ) - runCurrent() - - // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED - runTransitionAndSetWakefulness( - KeyguardState.GONE, - KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ) - - // WHEN the lockscreen hosted dream stops - keyguardRepository.setIsActiveDreamLockscreenHosted(false) - advanceTimeBy(100L) - - assertThat(transitionRepository) - .startedTransition( - to = KeyguardState.LOCKSCREEN, - from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ownerName = "FromDreamingLockscreenHostedTransitionInteractor", - animatorAssertion = { it.isNotNull() }, - ) - - coroutineContext.cancelChildren() - } - - @Test - @DisableSceneContainer - fun dreamingLockscreenHostedToGone() = - testScope.runTest { - // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED - runTransitionAndSetWakefulness( - KeyguardState.GONE, - KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ) - - // WHEN biometrics succeeds with wake and unlock from dream mode - keyguardRepository.setBiometricUnlockState( - BiometricUnlockMode.WAKE_AND_UNLOCK_FROM_DREAM - ) - runCurrent() - - assertThat(transitionRepository) - .startedTransition( - to = KeyguardState.GONE, - from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ownerName = "FromDreamingLockscreenHostedTransitionInteractor", - animatorAssertion = { it.isNotNull() }, - ) - - coroutineContext.cancelChildren() - } - - @Test - @DisableSceneContainer - fun dreamingLockscreenHostedToPrimaryBouncer() = - testScope.runTest { - // GIVEN a device dreaming with lockscreen hosted dream and not dozing - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - runCurrent() - - // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED - runTransitionAndSetWakefulness( - KeyguardState.GONE, - KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ) - - // WHEN the primary bouncer is set to show - bouncerRepository.setPrimaryShow(true) - runCurrent() - - assertThat(transitionRepository) - .startedTransition( - to = KeyguardState.PRIMARY_BOUNCER, - from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ownerName = "FromDreamingLockscreenHostedTransitionInteractor", - animatorAssertion = { it.isNotNull() }, - ) - - coroutineContext.cancelChildren() - } - - @Test - @DisableSceneContainer - fun dreamingLockscreenHostedToDozing() = - testScope.runTest { - // GIVEN a device is dreaming with lockscreen hosted dream - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - runCurrent() - - // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED - runTransitionAndSetWakefulness( - KeyguardState.GONE, - KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ) - - // WHEN the device begins to sleep - keyguardRepository.setIsActiveDreamLockscreenHosted(false) - keyguardRepository.setDozeTransitionModel( - DozeTransitionModel(from = DozeStateModel.INITIALIZED, to = DozeStateModel.DOZE) - ) - runCurrent() - - assertThat(transitionRepository) - .startedTransition( - to = KeyguardState.DOZING, - from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ownerName = "FromDreamingLockscreenHostedTransitionInteractor", - animatorAssertion = { it.isNotNull() }, - ) - - coroutineContext.cancelChildren() - } - - @Test - @DisableSceneContainer - fun dreamingLockscreenHostedToOccluded() = - testScope.runTest { - // GIVEN device is dreaming with lockscreen hosted dream and not occluded - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - keyguardRepository.setKeyguardOccluded(false) - runCurrent() - - // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED - runTransitionAndSetWakefulness( - KeyguardState.GONE, - KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ) - - // WHEN the keyguard is occluded and the lockscreen hosted dream stops - keyguardRepository.setIsActiveDreamLockscreenHosted(false) - keyguardRepository.setKeyguardOccluded(true) - runCurrent() - - assertThat(transitionRepository) - .startedTransition( - to = KeyguardState.OCCLUDED, - from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ownerName = "FromDreamingLockscreenHostedTransitionInteractor", - animatorAssertion = { it.isNotNull() }, - ) - - coroutineContext.cancelChildren() - } - - @Test fun dozingToLockscreen() = testScope.runTest { // GIVEN a prior transition has run to DOZING diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt index b6ec6a67b212..e24bb0432ed5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.fakeLightRevealScrimRepository import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_DURATION @@ -31,7 +32,6 @@ import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.fakePowerRepository -import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.statusbar.LightRevealEffect @@ -98,7 +98,7 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( to = KeyguardState.LOCKSCREEN, - transitionState = TransitionState.RUNNING + transitionState = TransitionState.RUNNING, ) ) runCurrent() @@ -110,7 +110,7 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep( to = KeyguardState.LOCKSCREEN, - transitionState = TransitionState.STARTED + transitionState = TransitionState.STARTED, ) ) runCurrent() @@ -147,19 +147,29 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { assertThat(fakeLightRevealScrimRepository.revealAnimatorRequests.last()) .isEqualTo( - RevealAnimatorRequest( - reveal = false, - duration = DEFAULT_REVEAL_DURATION - ) + RevealAnimatorRequest(reveal = false, duration = DEFAULT_REVEAL_DURATION) ) } + @Test + fun supportsAmbientMode() = + kosmos.testScope.runTest { + val maxAlpha by collectLastValue(underTest.maxAlpha) + assertThat(maxAlpha).isEqualTo(1f) + + underTest.setWallpaperSupportsAmbientMode(true) + assertThat(maxAlpha).isLessThan(1f) + + underTest.setWallpaperSupportsAmbientMode(false) + assertThat(maxAlpha).isEqualTo(1f) + } + private fun updateWakefulness(goToSleepReason: WakeSleepReason) { fakePowerRepository.updateWakefulness( rawState = WakefulnessState.STARTING_TO_SLEEP, lastWakeReason = WakeSleepReason.POWER_BUTTON, lastSleepReason = goToSleepReason, - powerButtonLaunchGestureTriggered = false + powerButtonLaunchGestureTriggered = false, ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt index c4eabd84e031..380060865282 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt @@ -16,7 +16,6 @@ package com.android.systemui.keyguard.ui.binder -import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper import android.view.View import android.view.layoutInflater @@ -24,7 +23,6 @@ import android.view.mockedLayoutInflater import android.view.windowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.domain.interactor.givenCanShowAlternateBouncer import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -63,7 +61,7 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { kosmos.mockedLayoutInflater.inflate( eq(R.layout.alternate_bouncer), isNull(), - anyBoolean() + anyBoolean(), ) ) .thenReturn(mockedAltBouncerView) @@ -71,7 +69,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun addViewToWindowManager() { testScope.runTest { kosmos.givenCanShowAlternateBouncer() @@ -85,7 +82,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun viewRemovedImmediatelyIfAlreadyAttachedToWindow() { testScope.runTest { kosmos.givenCanShowAlternateBouncer() @@ -107,7 +103,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun viewNotRemovedUntilAttachedToWindow() { testScope.runTest { kosmos.givenCanShowAlternateBouncer() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt index c1bd37811787..5d9548057bae 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository import com.android.systemui.coroutines.collectLastValue @@ -34,7 +33,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @ExperimentalCoroutinesApi @RunWith(AndroidJUnit4::class) @@ -49,7 +47,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { @Test fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() = testScope.runTest { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) fingerprintPropertyRepository.supportsUdfps() @@ -64,28 +61,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { assertThat(alternateBouncerWindowRequired).isTrue() transitionRepository.sendTransitionSteps( - listOf( - stepFromAlternateBouncer(1.0f, TransitionState.FINISHED), - ), - testScope, - ) - assertThat(alternateBouncerWindowRequired).isFalse() - } - - @Test - fun deviceEntryUdfpsFlagDisabled_alternateBouncerWindowRequiredFalse() = - testScope.runTest { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - val alternateBouncerWindowRequired by - collectLastValue(underTest.alternateBouncerWindowRequired) - fingerprintPropertyRepository.supportsUdfps() - transitionRepository.sendTransitionSteps( - listOf( - stepFromAlternateBouncer(0f, TransitionState.STARTED), - stepFromAlternateBouncer(.4f), - stepFromAlternateBouncer(.6f), - stepFromAlternateBouncer(1f), - ), + listOf(stepFromAlternateBouncer(1.0f, TransitionState.FINISHED)), testScope, ) assertThat(alternateBouncerWindowRequired).isFalse() @@ -94,7 +70,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { @Test fun lockscreenTransition_alternateBouncerWindowRequiredFalse() = testScope.runTest { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) fingerprintPropertyRepository.supportsUdfps() @@ -113,7 +88,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { @Test fun rearFps_alternateBouncerWindowRequiredFalse() = testScope.runTest { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) fingerprintPropertyRepository.supportsRearFps() @@ -131,7 +105,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { private fun stepFromAlternateBouncer( value: Float, - state: TransitionState = TransitionState.RUNNING + state: TransitionState = TransitionState.RUNNING, ): TransitionStep { return step( from = KeyguardState.ALTERNATE_BOUNCER, @@ -143,7 +117,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { private fun stepFromDozingToLockscreen( value: Float, - state: TransitionState = TransitionState.RUNNING + state: TransitionState = TransitionState.RUNNING, ): TransitionStep { return step( from = KeyguardState.DOZING, @@ -157,14 +131,14 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { from: KeyguardState, to: KeyguardState, value: Float, - transitionState: TransitionState + transitionState: TransitionState, ): TransitionStep { return TransitionStep( from = from, to = to, value = value, transitionState = transitionState, - ownerName = "AlternateBouncerViewModelTest" + ownerName = "AlternateBouncerViewModelTest", ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt index 14d60943149f..e5bdc2e56b11 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt @@ -54,7 +54,7 @@ class PaginatedGridRepositoryTest : SysuiTestCase() { private fun setRowsInConfig(rows: Int) = with(kosmos) { testCase.context.orCreateTestableResources.addOverride( - R.integer.quick_settings_max_rows, + R.integer.quick_settings_paginated_grid_num_rows, rows, ) fakeConfigurationRepository.onConfigurationChange() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt index fd1f52b9d8c8..ec0773f79328 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt @@ -16,7 +16,6 @@ package com.android.systemui.qs.panels.data.repository -import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -25,7 +24,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testScope import com.android.systemui.res.R -import com.android.systemui.shade.shared.flag.DualShade +import com.android.systemui.shade.data.repository.fakeShadeRepository import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest @@ -59,15 +58,22 @@ class QSColumnsRepositoryTest : SysuiTestCase() { } @Test - @EnableFlags(DualShade.FLAG_NAME) fun withDualShade_returnsCorrectValue() = with(kosmos) { testScope.runTest { - val latest by collectLastValue(underTest.columns) + val latest by collectLastValue(underTest.dualShadeColumns) + assertThat(latest).isEqualTo(4) + } + } + + @Test + fun withSplitShade_returnsCorrectValue() = + with(kosmos) { + testScope.runTest { + val latest by collectLastValue(underTest.splitShadeColumns) + fakeShadeRepository.setShadeLayoutWide(true) - setColumnsInConfig(8, id = R.integer.quick_settings_dual_shade_num_columns) - // Asserts config changes are ignored assertThat(latest).isEqualTo(4) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt index ae6f576bcf3e..cda3d488cb1e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt @@ -54,7 +54,7 @@ class QuickQuickSettingsRowRepositoryTest : SysuiTestCase() { private fun setRowsInConfig(rows: Int) = with(kosmos) { testCase.context.orCreateTestableResources.addOverride( - R.integer.quick_qs_panel_max_rows, + R.integer.quick_qs_paginated_grid_num_rows, rows, ) fakeConfigurationRepository.onConfigurationChange() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt new file mode 100644 index 000000000000..35f7504ead50 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.domain.interactor + +import android.content.res.mainResources +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.ui.data.repository.configurationRepository +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testCase +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.panels.data.repository.QSColumnsRepository +import com.android.systemui.qs.panels.data.repository.qsColumnsRepository +import com.android.systemui.res.R +import com.android.systemui.shade.data.repository.fakeShadeRepository +import com.android.systemui.shade.shared.flag.DualShade +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class QSColumnsInteractorTest : SysuiTestCase() { + private val kosmos = + testKosmos().apply { + testCase.context.orCreateTestableResources.addOverride( + R.integer.quick_settings_infinite_grid_num_columns, + 1, + ) + testCase.context.orCreateTestableResources.addOverride( + R.integer.quick_settings_dual_shade_num_columns, + 2, + ) + testCase.context.orCreateTestableResources.addOverride( + R.integer.quick_settings_split_shade_num_columns, + 3, + ) + qsColumnsRepository = QSColumnsRepository(mainResources, configurationRepository) + } + private lateinit var underTest: QSColumnsInteractor + + @Before + fun setUp() { + underTest = with(kosmos) { qsColumnsInteractor } + } + + @Test + @DisableFlags(DualShade.FLAG_NAME) + fun withSingleShade_returnsCorrectValue() = + with(kosmos) { + testScope.runTest { + val latest by collectLastValue(underTest.columns) + + assertThat(latest).isEqualTo(1) + } + } + + @Test + @EnableFlags(DualShade.FLAG_NAME) + fun withDualShade_returnsCorrectValue() = + with(kosmos) { + testScope.runTest { + val latest by collectLastValue(underTest.columns) + + assertThat(latest).isEqualTo(2) + } + } + + @Test + @DisableFlags(DualShade.FLAG_NAME) + fun withSplitShade_returnsCorrectValue() = + with(kosmos) { + testScope.runTest { + val latest by collectLastValue(underTest.columns) + + fakeShadeRepository.setShadeLayoutWide(true) + + assertThat(latest).isEqualTo(3) + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt index a1c0ef2789d5..2c894f9aa20f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt @@ -151,7 +151,7 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() { private fun Kosmos.setRows(rows: Int) { testCase.context.orCreateTestableResources.addOverride( - R.integer.quick_qs_panel_max_rows, + R.integer.quick_qs_paginated_grid_num_rows, rows, ) fakeConfigurationRepository.onConfigurationChange() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java index 7798f46fdb46..b23262a87e2e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java @@ -34,9 +34,9 @@ import android.graphics.drawable.Drawable; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.service.quicksettings.Tile; -import android.testing.UiThreadTest; import android.widget.ImageView; +import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt new file mode 100644 index 000000000000..cdf6bda91301 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.tiles.impl.hearingdevices.domain + +import android.graphics.drawable.TestStubDrawable +import android.widget.Switch +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class HearingDevicesTileMapperTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val qsTileConfig = kosmos.qsHearingDevicesTileConfig + private val mapper by lazy { + HearingDevicesTileMapper( + context.orCreateTestableResources + .apply { addOverride(R.drawable.qs_hearing_devices_icon, TestStubDrawable()) } + .resources, + context.theme, + ) + } + + @Test + fun map_anyActiveHearingDevice_anyPairedHearingDevice_activeState() { + val tileState: QSTileState = + mapper.map( + qsTileConfig, + HearingDevicesTileModel( + isAnyActiveHearingDevice = true, + isAnyPairedHearingDevice = true, + ), + ) + val expectedState = + createHearingDevicesTileState( + QSTileState.ActivationState.ACTIVE, + context.getString(R.string.quick_settings_hearing_devices_connected), + ) + QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState) + } + + @Test + fun map_noActiveHearingDevice_anyPairedHearingDevice_inactiveState() { + val tileState: QSTileState = + mapper.map( + qsTileConfig, + HearingDevicesTileModel( + isAnyActiveHearingDevice = false, + isAnyPairedHearingDevice = true, + ), + ) + val expectedState = + createHearingDevicesTileState( + QSTileState.ActivationState.INACTIVE, + context.getString(R.string.quick_settings_hearing_devices_disconnected), + ) + QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState) + } + + @Test + fun map_noActiveHearingDevice_noPairedHearingDevice_inactiveState() { + val tileState: QSTileState = + mapper.map( + qsTileConfig, + HearingDevicesTileModel( + isAnyActiveHearingDevice = false, + isAnyPairedHearingDevice = false, + ), + ) + val expectedState = + createHearingDevicesTileState(QSTileState.ActivationState.INACTIVE, secondaryLabel = "") + QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState) + } + + private fun createHearingDevicesTileState( + activationState: QSTileState.ActivationState, + secondaryLabel: String, + ): QSTileState { + val label = context.getString(R.string.quick_settings_hearing_devices_label) + val iconRes = R.drawable.qs_hearing_devices_icon + return QSTileState( + { Icon.Loaded(context.getDrawable(iconRes)!!, null) }, + iconRes, + label, + activationState, + secondaryLabel, + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), + label, + null, + QSTileState.SideViewIcon.Chevron, + QSTileState.EnabledState.ENABLED, + Switch::class.qualifiedName, + ) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt new file mode 100644 index 000000000000..1dfa2cd26491 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor + +import android.os.UserHandle +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.annotations.EnabledOnRavenwood +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.Flags +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.android.systemui.statusbar.policy.fakeBluetoothController +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.whenever + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@EnabledOnRavenwood +@RunWith(AndroidJUnit4::class) +class HearingDevicesTileDataInteractorTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val testScope = kosmos.testScope + private val testUser = UserHandle.of(1) + + private val controller = kosmos.fakeBluetoothController + private lateinit var underTest: HearingDevicesTileDataInteractor + + @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule() + @Mock private lateinit var checker: HearingDevicesChecker + + @Before + fun setup() { + underTest = HearingDevicesTileDataInteractor(testScope.testScheduler, controller, checker) + } + + @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG) + @Test + fun availability_flagEnabled_returnTrue() = + testScope.runTest { + val availability by collectLastValue(underTest.availability(testUser)) + + assertThat(availability).isTrue() + } + + @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG) + @Test + fun availability_flagDisabled_returnFalse() = + testScope.runTest { + val availability by collectLastValue(underTest.availability(testUser)) + + assertThat(availability).isFalse() + } + + @Test + fun tileData_bluetoothStateChanged_dataMatchesChecker() = + testScope.runTest { + val flowValues: List<HearingDevicesTileModel> by + collectValues( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + runCurrent() + assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup() + + whenever(checker.isAnyPairedHearingDevice).thenReturn(false) + whenever(checker.isAnyActiveHearingDevice).thenReturn(false) + controller.isBluetoothEnabled = false + runCurrent() + assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value + + whenever(checker.isAnyPairedHearingDevice).thenReturn(true) + whenever(checker.isAnyActiveHearingDevice).thenReturn(false) + controller.isBluetoothEnabled = true + runCurrent() + assertThat(flowValues.size).isEqualTo(2) + + whenever(checker.isAnyPairedHearingDevice).thenReturn(true) + whenever(checker.isAnyActiveHearingDevice).thenReturn(true) + controller.isBluetoothEnabled = true + runCurrent() + assertThat(flowValues.size).isEqualTo(3) + + assertThat(flowValues.map { it.isAnyPairedHearingDevice }) + .containsExactly(false, true, true) + .inOrder() + assertThat(flowValues.map { it.isAnyActiveHearingDevice }) + .containsExactly(false, false, true) + .inOrder() + } + + @Test + fun tileData_bluetoothDeviceChanged_dataMatchesChecker() = + testScope.runTest { + val flowValues: List<HearingDevicesTileModel> by + collectValues( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + runCurrent() + assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup() + + whenever(checker.isAnyPairedHearingDevice).thenReturn(false) + whenever(checker.isAnyActiveHearingDevice).thenReturn(false) + controller.onBluetoothDevicesChanged() + runCurrent() + assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value + + whenever(checker.isAnyPairedHearingDevice).thenReturn(true) + whenever(checker.isAnyActiveHearingDevice).thenReturn(false) + controller.onBluetoothDevicesChanged() + runCurrent() + assertThat(flowValues.size).isEqualTo(2) + + whenever(checker.isAnyPairedHearingDevice).thenReturn(true) + whenever(checker.isAnyActiveHearingDevice).thenReturn(true) + controller.onBluetoothDevicesChanged() + runCurrent() + assertThat(flowValues.size).isEqualTo(3) + + assertThat(flowValues.map { it.isAnyPairedHearingDevice }) + .containsExactly(false, true, true) + .inOrder() + assertThat(flowValues.map { it.isAnyActiveHearingDevice }) + .containsExactly(false, false, true) + .inOrder() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt new file mode 100644 index 000000000000..00ee1c36590c --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor + +import android.platform.test.annotations.EnabledOnRavenwood +import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager +import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.verify + +@SmallTest +@EnabledOnRavenwood +@RunWith(AndroidJUnit4::class) +class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val testScope = kosmos.testScope + private val inputHandler = FakeQSTileIntentUserInputHandler() + + private lateinit var underTest: HearingDevicesTileUserActionInteractor + + @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule() + @Mock private lateinit var dialogManager: HearingDevicesDialogManager + + @Before + fun setUp() { + underTest = + HearingDevicesTileUserActionInteractor( + testScope.coroutineContext, + inputHandler, + dialogManager, + ) + } + + @Test + fun handleClick_launchDialog() = + testScope.runTest { + val input = + HearingDevicesTileModel( + isAnyActiveHearingDevice = true, + isAnyPairedHearingDevice = true, + ) + + underTest.handleInput(QSTileInputTestKtx.click(input)) + + verify(dialogManager).showDialog(anyOrNull(), eq(LAUNCH_SOURCE_QS_TILE)) + } + + @Test + fun handleLongClick_launchSettings() = + testScope.runTest { + val input = + HearingDevicesTileModel( + isAnyActiveHearingDevice = true, + isAnyPairedHearingDevice = true, + ) + + underTest.handleInput(QSTileInputTestKtx.longClick(input)) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_HEARING_DEVICES_SETTINGS) + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt index 04ca38fa4343..3e40c5ca797c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt @@ -22,6 +22,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon +import com.android.systemui.defaultDeviceState +import com.android.systemui.deviceStateManager +import com.android.systemui.foldedDeviceStateList import com.android.systemui.kosmos.Kosmos import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel @@ -30,11 +33,11 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileState import com.android.systemui.res.R import com.android.systemui.statusbar.policy.DevicePostureController import com.android.systemui.statusbar.policy.devicePostureController -import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @@ -42,13 +45,17 @@ class RotationLockTileMapperTest : SysuiTestCase() { private val kosmos = Kosmos() private val rotationLockTileConfig = kosmos.qsRotationLockTileConfig private val devicePostureController = kosmos.devicePostureController + private val deviceStateManager = kosmos.deviceStateManager private lateinit var mapper: RotationLockTileMapper @Before fun setup() { + deviceStateManager whenever(devicePostureController.devicePosture) .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED) + whenever(deviceStateManager.supportedDeviceStates) + .thenReturn(listOf(kosmos.defaultDeviceState)) mapper = RotationLockTileMapper( @@ -64,7 +71,8 @@ class RotationLockTileMapperTest : SysuiTestCase() { } .resources, context.theme, - devicePostureController + devicePostureController, + deviceStateManager ) } @@ -162,6 +170,7 @@ class RotationLockTileMapperTest : SysuiTestCase() { intArrayOf(1, 2, 3) ) } + whenever(deviceStateManager.supportedDeviceStates).thenReturn(kosmos.foldedDeviceStateList) } private fun createRotationLockTileState( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 0454317b5f04..d1d3b9b31a08 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -68,13 +68,13 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.LatencyTracker; +import com.android.keyguard.EmptyLockIconViewController; import com.android.keyguard.KeyguardClockSwitch; import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardSliceViewController; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardStatusViewController; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.LegacyLockIconViewController; import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; @@ -108,7 +108,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver; import com.android.systemui.keyguard.ui.view.KeyguardRootView; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; -import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel; @@ -271,6 +270,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected KeyguardUserSwitcherController mKeyguardUserSwitcherController; @Mock protected KeyguardStatusViewComponent mKeyguardStatusViewComponent; @Mock protected KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; + @Mock protected EmptyLockIconViewController mLockIconViewController; @Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent; @Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController; @Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController; @@ -285,7 +285,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected AmbientState mAmbientState; @Mock protected UserManager mUserManager; @Mock protected UiEventLogger mUiEventLogger; - @Mock protected LegacyLockIconViewController mLockIconViewController; @Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator; @Mock protected KeyguardRootView mKeyguardRootView; @Mock protected View mKeyguardRootViewChild; @@ -328,8 +327,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel; @Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel; - @Mock protected GoneToDreamingLockscreenHostedTransitionViewModel - mGoneToDreamingLockscreenHostedTransitionViewModel; @Mock protected PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor; @@ -397,7 +394,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false); mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR); - mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); mMainDispatcher = getMainDispatcher(); KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps = @@ -588,10 +584,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { when(mGoneToDreamingTransitionViewModel.lockscreenTranslationY(anyInt())) .thenReturn(emptyFlow()); - // Gone->Dreaming lockscreen hosted - when(mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha()) - .thenReturn(emptyFlow()); - // Lockscreen->Occluded when(mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha()) .thenReturn(emptyFlow()); @@ -687,6 +679,9 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { when(longPressHandlingView.getResources()).thenReturn(longPressHandlingViewRes); when(longPressHandlingViewRes.getString(anyInt())).thenReturn(""); + when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild); + when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView); + mNotificationPanelViewController = new NotificationPanelViewController( mView, mMainHandler, @@ -750,7 +745,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mOccludedToLockscreenTransitionViewModel, mLockscreenToDreamingTransitionViewModel, mGoneToDreamingTransitionViewModel, - mGoneToDreamingLockscreenHostedTransitionViewModel, mLockscreenToOccludedTransitionViewModel, mPrimaryBouncerToGoneTransitionViewModel, mMainDispatcher, @@ -852,7 +846,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mNotificationPanelViewController.mBottomAreaShadeAlphaAnimator.cancel(); mNotificationPanelViewController.cancelHeightAnimator(); leakedAnimators = mNotificationPanelViewController.mTestSetOfAnimatorsUsed.stream() - .filter(Animator::isRunning).toList(); + .filter(Animator::isRunning).toList(); mNotificationPanelViewController.mTestSetOfAnimatorsUsed.forEach(Animator::cancel); } if (mMainHandler != null) { @@ -869,11 +863,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0); when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom); when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom); - when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding)); - when(mKeyguardRootViewChild.getTop()).thenReturn((int) (stackBottom - lockIconPadding)); - when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild); - when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView); when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding)) .thenReturn(indicationPadding); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 43dbb40d7721..ec75972aecfe 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -217,7 +217,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(5); - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(5); } @@ -235,7 +234,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); } @@ -253,7 +251,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); } @@ -271,7 +268,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(2); - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(2); } @@ -289,7 +285,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); } @@ -389,7 +384,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo @Test @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) public void alternateBouncerVisible_onTouchEvent_notHandled() { - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); // GIVEN alternate bouncer is visible when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 6f2302a22d7b..adc336d18245 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -30,7 +30,6 @@ import android.view.ViewGroup import android.view.ViewTreeObserver import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityContainerController -import com.android.keyguard.LegacyLockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.Flags import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT @@ -43,7 +42,6 @@ import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.FakeFeatureFlagsClassic -import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED import com.android.systemui.flags.Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION import com.android.systemui.flags.andSceneContainer import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler @@ -54,7 +52,6 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.res.R -import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.statusbar.DragDownHelper @@ -71,7 +68,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.PhoneStatusBarViewController -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider @@ -80,6 +76,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow @@ -98,11 +95,10 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters -import java.util.Optional -import org.mockito.Mockito.`when` as whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -125,16 +121,12 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var ambientState: AmbientState @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController - @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController @Mock private lateinit var quickSettingsController: QuickSettingsControllerImpl @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController - @Mock private lateinit var lockIconViewController: LegacyLockIconViewController @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController @Mock private lateinit var pulsingGestureListener: PulsingGestureListener - @Mock - private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener @Mock private lateinit var notificationInsetsController: NotificationInsetsController @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController @Mock(answer = Answers.RETURNS_DEEP_STUBS) @@ -144,7 +136,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @Mock private lateinit var unfoldTransitionProgressProvider: - Optional<UnfoldTransitionProgressProvider> + Optional<UnfoldTransitionProgressProvider> @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock lateinit var dragDownHelper: DragDownHelper @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor @@ -176,20 +168,16 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : MockitoAnnotations.initMocks(this) whenever(view.bottom).thenReturn(VIEW_BOTTOM) whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container)) - .thenReturn(mock(ViewGroup::class.java)) + .thenReturn(mock(ViewGroup::class.java)) whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java))) - .thenReturn(keyguardBouncerComponent) + .thenReturn(keyguardBouncerComponent) whenever(keyguardBouncerComponent.securityContainerController) - .thenReturn(keyguardSecurityContainerController) + .thenReturn(keyguardSecurityContainerController) whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING))) - .thenReturn(emptyFlow<TransitionStep>()) + .thenReturn(emptyFlow<TransitionStep>()) featureFlagsClassic = FakeFeatureFlagsClassic() featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) - featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) - if (!SceneContainerFlag.isEnabled) { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - } mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES) testScope = TestScope() @@ -208,9 +196,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : panelExpansionInteractor, ShadeExpansionStateManager(), stackScrollLayoutController, - statusBarKeyguardViewManager, statusBarWindowStateController, - lockIconViewController, centralSurfaces, dozeServiceHost, dozeScrimController, @@ -223,7 +209,6 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : shadeLogger, dumpManager, pulsingGestureListener, - mLockscreenHostedDreamGestureListener, keyguardTransitionInteractor, mGlanceableHubContainerController, notificationLaunchAnimationInteractor, @@ -233,7 +218,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : quickSettingsController, primaryBouncerInteractor, alternateBouncerInteractor, - mock(BouncerViewBinder::class.java) + mock(BouncerViewBinder::class.java), ) underTest.setupExpandedStatusBar() underTest.setDragDownHelper(dragDownHelper) @@ -294,7 +279,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) whenever(phoneStatusBarViewController.sendTouchToView(DOWN_EVENT)).thenReturn(true) val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -309,7 +294,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) // Item we're testing whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(false) @@ -327,7 +312,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) // Item we're testing whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(false) + .thenReturn(false) val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -341,7 +326,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) // Item we're testing whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false) @@ -358,7 +343,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) // Down event first interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -379,7 +364,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : // GIVEN touch dispatcher in a state that returns true underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) - .thenReturn(true) + .thenReturn(true) assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue() // WHEN launch animation is running for 2 seconds @@ -432,47 +417,13 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : } @Test - fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() { - // down event should be intercepted by keyguardViewManager - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(true) - - // Then touch should not be intercepted - val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT) - assertThat(shouldIntercept).isTrue() - } - - @Test - @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) - fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() { - // GIVEN dozing - whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) - // AND alternate bouncer doesn't want the touch - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND quick settings controller doesn't want it - whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(false) - // AND the lock icon wants the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true) - - // THEN touch should NOT be intercepted by NotificationShade - assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse() - } - - @Test @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() { // GIVEN dozing whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) - // AND alternate bouncer doesn't want the touch - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND the lock icon does NOT want the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false) // AND quick settings controller doesn't want it whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(false) + .thenReturn(false) // THEN touch should be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() @@ -483,14 +434,9 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : fun shouldInterceptTouchEvent_dozing_touchInStatusBar_touchIntercepted() { // GIVEN dozing whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) - // AND alternate bouncer doesn't want the touch - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND the lock icon does NOT want the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false) // AND quick settings controller DOES want it whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(true) + .thenReturn(true) // THEN touch should be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() @@ -503,20 +449,13 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) // AND pulsing whenever(dozeServiceHost.isPulsing()).thenReturn(true) - // AND status bar doesn't want it - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND shade is not fully expanded (mock is false by default) - // AND the lock icon does NOT want the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false) // AND quick settings controller DOES want it whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(true) + .thenReturn(true) // AND bouncer is not showing whenever(centralSurfaces.isBouncerShowing()).thenReturn(false) // AND panel view controller wants it - whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT)) - .thenReturn(true) + whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT)).thenReturn(true) // THEN touch should be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() @@ -589,12 +528,10 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : underTest.setupCommunalHubLayout() // Simluate attaching the view so flow collection starts. - val onAttachStateChangeListenerArgumentCaptor = ArgumentCaptor.forClass( - View.OnAttachStateChangeListener::class.java - ) - verify(view, atLeast(1)).addOnAttachStateChangeListener( - onAttachStateChangeListenerArgumentCaptor.capture() - ) + val onAttachStateChangeListenerArgumentCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java) + verify(view, atLeast(1)) + .addOnAttachStateChangeListener(onAttachStateChangeListenerArgumentCaptor.capture()) for (listener in onAttachStateChangeListenerArgumentCaptor.allValues) { listener.onViewAttachedToWindow(view) } @@ -608,7 +545,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : @RequiresFlagsDisabled(Flags.FLAG_COMMUNAL_HUB) fun doesNotSetupCommunalHubLayout_whenFlagDisabled() { whenever(mGlanceableHubContainerController.communalAvailable()) - .thenReturn(MutableStateFlow(false)) + .thenReturn(MutableStateFlow(false)) val mockCommunalPlaceholder = mock(View::class.java) val fakeViewIndex = 20 diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index ca29dd98a637..d61320e22e14 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -23,7 +23,6 @@ import android.widget.FrameLayout import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityContainerController -import com.android.keyguard.LegacyLockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.Flags as AConfigFlags import com.android.systemui.SysuiTestCase @@ -57,7 +56,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider @@ -104,19 +102,15 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController - @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController - @Mock private lateinit var lockIconViewController: LegacyLockIconViewController @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController @Mock private lateinit var ambientState: AmbientState @Mock private lateinit var shadeLogger: ShadeLogger @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var pulsingGestureListener: PulsingGestureListener @Mock private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent - @Mock - private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener @Mock private lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock private lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock @@ -160,8 +154,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { val featureFlags = FakeFeatureFlags() featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) - featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) - mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES) testScope = TestScope() controller = @@ -176,9 +168,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { panelExpansionInteractor, ShadeExpansionStateManager(), notificationStackScrollLayoutController, - statusBarKeyguardViewManager, statusBarWindowStateController, - lockIconViewController, centralSurfaces, dozeServiceHost, dozeScrimController, @@ -191,7 +181,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { shadeLogger, dumpManager, pulsingGestureListener, - mLockscreenHostedDreamGestureListener, keyguardTransitionInteractor, mGlanceableHubContainerController, NotificationLaunchAnimationInteractor(NotificationLaunchAnimationRepository()), @@ -221,48 +210,18 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { } @Test - fun testInterceptTouchWhenShowingAltAuth() = - testScope.runTest { - captureInteractionEventHandler() - - // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept - whenever(statusBarStateController.isDozing).thenReturn(false) - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())).thenReturn(true) - whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false) - - // THEN we should intercept touch - assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isTrue() - } - - @Test fun testNoInterceptTouch() = testScope.runTest { captureInteractionEventHandler() - // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept + // WHEN not dozing, drag down helper doesn't want to intercept whenever(statusBarStateController.isDozing).thenReturn(false) - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())) - .thenReturn(false) whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false) // THEN we shouldn't intercept touch assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isFalse() } - @Test - fun testHandleTouchEventWhenShowingAltAuth() = - testScope.runTest { - captureInteractionEventHandler() - - // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept - whenever(statusBarStateController.isDozing).thenReturn(false) - whenever(statusBarKeyguardViewManager.onTouch(any())).thenReturn(true) - whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false) - - // THEN we should handle the touch - assertThat(interactionEventHandler.handleTouchEvent(mock())).isTrue() - } - private fun captureInteractionEventHandler() { verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture()) interactionEventHandler = interactionEventHandlerCaptor.value diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java index 59678a2f8c90..44782529e5c3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX; -import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT; import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; @@ -273,7 +272,6 @@ public class KeyguardIndicationControllerBaseTest extends SysuiTestCase { mFlags = new FakeFeatureFlags(); mFlags.set(KEYGUARD_TALKBACK_FIX, true); - mFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); mController = new KeyguardIndicationController( mContext, mTestableLooper.getLooper(), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt index 118dea6376bb..69a76271f726 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel import android.content.packageManager import android.graphics.drawable.BitmapDrawable import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.commandline.CommandRegistry @@ -40,13 +40,13 @@ import org.mockito.kotlin.any import org.mockito.kotlin.whenever @SmallTest -class DemoRonChipViewModelTest : SysuiTestCase() { +class DemoNotifChipViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val commandRegistry = kosmos.commandRegistry private val pw = PrintWriter(StringWriter()) - private val underTest = kosmos.demoRonChipViewModel + private val underTest = kosmos.demoNotifChipViewModel @Before fun setUp() { @@ -56,61 +56,61 @@ class DemoRonChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @DisableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_flagOff_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) - addDemoRonChip() + addDemoNotifChip() assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_noPackage_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) - commandRegistry.onShellCommand(pw, arrayOf("demo-ron")) + commandRegistry.onShellCommand(pw, arrayOf("demo-notif")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_hasPackage_shown() = testScope.runTest { val latest by collectLastValue(underTest.chip) - commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui")) + commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_hasText_shownWithText() = testScope.runTest { val latest by collectLastValue(underTest.chip) commandRegistry.onShellCommand( pw, - arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test") + arrayOf("demo-notif", "-p", "com.android.systemui", "-t", "test"), ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_supportsColor() = testScope.runTest { val latest by collectLastValue(underTest.chip) commandRegistry.onShellCommand( pw, - arrayOf("demo-ron", "-p", "com.android.systemui", "-c", "#434343") + arrayOf("demo-notif", "-p", "com.android.systemui", "-c", "#434343"), ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) @@ -119,28 +119,28 @@ class DemoRonChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_hasHideArg_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) // First, show a chip - addDemoRonChip() + addDemoNotifChip() assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) // Then, hide the chip - commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide")) + commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "--hide")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } - private fun addDemoRonChip() { - Companion.addDemoRonChip(commandRegistry, pw) + private fun addDemoNotifChip() { + addDemoNotifChip(commandRegistry, pw) } companion object { - fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) { - commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui")) + fun addDemoNotifChip(commandRegistry: CommandRegistry, pw: PrintWriter) { + commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui")) } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt new file mode 100644 index 000000000000..eb5d9318c88f --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.ui.viewmodel + +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.statusbar.notification.data.model.activeNotificationModel +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.runner.RunWith +import org.mockito.kotlin.mock + +@SmallTest +@RunWith(AndroidJUnit4::class) +@OptIn(ExperimentalCoroutinesApi::class) +@EnableFlags(StatusBarNotifChips.FLAG_NAME) +class NotifChipsViewModelTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val activeNotificationListRepository = kosmos.activeNotificationListRepository + + private val underTest = kosmos.notifChipsViewModel + + @Test + fun chips_noNotifs_empty() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + setNotifs(emptyList()) + + assertThat(latest).isEmpty() + } + + @Test + fun chips_notifMissingStatusBarChipIconView_empty() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = null))) + + assertThat(latest).isEmpty() + } + + @Test + fun chips_oneNotif_statusBarIconViewMatches() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val icon = mock<StatusBarIconView>() + setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon))) + + assertThat(latest).hasSize(1) + val chip = latest!![0] + assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java) + assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon)) + } + + @Test + fun chips_twoNotifs_twoChips() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "notif1", statusBarChipIcon = firstIcon), + activeNotificationModel(key = "notif2", statusBarChipIcon = secondIcon), + ) + ) + + assertThat(latest).hasSize(2) + assertIsNotifChip(latest!![0], firstIcon) + assertIsNotifChip(latest!![1], secondIcon) + } + + private fun setNotifs(notifs: List<ActiveNotificationModel>) { + activeNotificationListRepository.activeNotifications.value = + ActiveNotificationsStore.Builder() + .apply { notifs.forEach { addIndividualNotif(it) } } + .build() + testScope.runCurrent() + } + + companion object { + fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) { + assertThat(latest) + .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java) + assertThat((latest as OngoingActivityChipModel.Shown).icon) + .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon)) + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index 26ce7b956fde..e96def6d43a3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -25,7 +25,6 @@ import android.platform.test.annotations.DisableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue @@ -40,7 +39,8 @@ import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.phone.SystemUIDialog @@ -66,13 +66,11 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -/** - * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is disabled. - */ +/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is disabled. */ @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) -@DisableFlags(FLAG_STATUS_BAR_RON_CHIPS) +@DisableFlags(StatusBarNotifChips.FLAG_NAME) class OngoingActivityChipsViewModelTest : SysuiTestCase() { private val kosmos = Kosmos().also { it.testCase = this } private val testScope = kosmos.testScope @@ -99,11 +97,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) - kosmos.demoRonChipViewModel.start() + kosmos.demoNotifChipViewModel.start() val icon = BitmapDrawable( context.resources, - Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888), ) whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon) } @@ -325,7 +323,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { latest: OngoingActivityChipModel?, chipView: View, dialog: SystemUIDialog, - kosmos: Kosmos + kosmos: Kosmos, ): DialogInterface.OnClickListener { // Capture the action that would get invoked when the user clicks "Stop" on the dialog lateinit var dialogStopAction: DialogInterface.OnClickListener diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt index 631120b39805..b12d7c57e1fd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt @@ -24,7 +24,6 @@ import android.platform.test.annotations.EnableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope @@ -34,10 +33,13 @@ import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer @@ -46,6 +48,10 @@ import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsVie import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog import com.android.systemui.statusbar.commandline.commandRegistry +import com.android.systemui.statusbar.notification.data.model.activeNotificationModel +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel @@ -67,14 +73,12 @@ import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.whenever -/** - * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is enabled. - */ +/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is enabled. */ @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) -@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) -class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { +@EnableFlags(StatusBarNotifChips.FLAG_NAME) +class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val systemClock = kosmos.fakeSystemClock @@ -83,6 +87,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { private val screenRecordState = kosmos.screenRecordRepository.screenRecordState private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState private val callRepo = kosmos.ongoingCallRepository + private val activeNotificationListRepository = kosmos.activeNotificationListRepository private val pw = PrintWriter(StringWriter()) @@ -103,16 +108,16 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) - kosmos.demoRonChipViewModel.start() + kosmos.demoNotifChipViewModel.start() val icon = BitmapDrawable( context.resources, - Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888), ) whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon) } - // Even though the `primaryChip` flow isn't used when the RONs flag is on, still test that the + // Even though the `primaryChip` flow isn't used when the notifs flag is on, still test that the // flow has the right behavior to verify that we don't break any existing functionality. @Test @@ -249,13 +254,13 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) val latest by collectLastValue(underTest.chips) assertIsScreenRecordChip(latest!!.primary) assertIsCallChip(latest!!.secondary) - // Demo RON chip is dropped + // Demo notif chip is dropped } @Test @@ -288,10 +293,101 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { } @Test + fun chips_singleNotifChip_primaryIsNotifSecondaryIsHidden() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val icon = mock<StatusBarIconView>() + setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon))) + + assertIsNotifChip(latest!!.primary, icon) + assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) + } + + @Test + fun chips_twoNotifChips_primaryAndSecondaryAreNotifsInOrder() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon), + activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon), + ) + ) + + assertIsNotifChip(latest!!.primary, firstIcon) + assertIsNotifChip(latest!!.secondary, secondIcon) + } + + @Test + fun chips_threeNotifChips_topTwoShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + val thirdIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon), + activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon), + activeNotificationModel(key = "thirdNotif", statusBarChipIcon = thirdIcon), + ) + ) + + assertIsNotifChip(latest!!.primary, firstIcon) + assertIsNotifChip(latest!!.secondary, secondIcon) + } + + @Test + fun chips_callAndNotifs_primaryIsCallSecondaryIsNotif() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + val firstIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon), + activeNotificationModel( + key = "secondNotif", + statusBarChipIcon = mock<StatusBarIconView>(), + ), + ) + ) + + assertIsCallChip(latest!!.primary) + assertIsNotifChip(latest!!.secondary, firstIcon) + } + + @Test + fun chips_screenRecordAndCallAndNotifs_notifsNotShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + screenRecordState.value = ScreenRecordModel.Recording + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = mock<StatusBarIconView>(), + ) + ) + ) + + assertIsScreenRecordChip(latest!!.primary) + assertIsCallChip(latest!!.secondary) + } + + @Test fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() = testScope.runTest { // Start with just the lowest priority chip shown - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) // And everything else hidden callRepo.setOngoingCallState(OngoingCallModel.NoCall) mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -299,7 +395,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.primaryChip) - assertIsDemoRonChip(latest) + assertIsDemoNotifChip(latest) // WHEN the higher priority call chip is added callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) @@ -333,7 +429,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) val latest by collectLastValue(underTest.primaryChip) @@ -355,15 +451,15 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { // WHEN the higher priority call is removed callRepo.setOngoingCallState(OngoingCallModel.NoCall) - // THEN the lower priority demo RON is used - assertIsDemoRonChip(latest) + // THEN the lower priority demo notif is used + assertIsDemoNotifChip(latest) } @Test fun chips_movesChipsAroundAccordingToPriority() = testScope.runTest { // Start with just the lowest priority chip shown - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) // And everything else hidden callRepo.setOngoingCallState(OngoingCallModel.NoCall) mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -371,16 +467,16 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chips) - assertIsDemoRonChip(latest!!.primary) + assertIsDemoNotifChip(latest!!.primary) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) // WHEN the higher priority call chip is added callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - // THEN the higher priority call chip is used as primary and demo ron is demoted to + // THEN the higher priority call chip is used as primary and demo notif is demoted to // secondary assertIsCallChip(latest!!.primary) - assertIsDemoRonChip(latest!!.secondary) + assertIsDemoNotifChip(latest!!.secondary) // WHEN the higher priority media projection chip is added mediaProjectionState.value = @@ -391,7 +487,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { ) // THEN the higher priority media projection chip is used as primary and call is demoted - // to secondary (and demo RON is dropped altogether) + // to secondary (and demo notif is dropped altogether) assertIsShareToAppChip(latest!!.primary) assertIsCallChip(latest!!.secondary) @@ -405,15 +501,15 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { screenRecordState.value = ScreenRecordModel.DoingNothing callRepo.setOngoingCallState(OngoingCallModel.NoCall) - // THEN media projection and demo RON remain + // THEN media projection and demo notif remain assertIsShareToAppChip(latest!!.primary) - assertIsDemoRonChip(latest!!.secondary) + assertIsDemoNotifChip(latest!!.secondary) // WHEN media projection is dropped mediaProjectionState.value = MediaProjectionState.NotProjecting - // THEN demo RON is promoted to primary - assertIsDemoRonChip(latest!!.primary) + // THEN demo notif is promoted to primary + assertIsDemoNotifChip(latest!!.primary) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @@ -526,9 +622,17 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false)) } - private fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) { + private fun assertIsDemoNotifChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).icon) .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java) } + + private fun setNotifs(notifs: List<ActiveNotificationModel>) { + activeNotificationListRepository.activeNotifications.value = + ActiveNotificationsStore.Builder() + .apply { notifs.forEach { addIndividualNotif(it) } } + .build() + testScope.runCurrent() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt new file mode 100644 index 000000000000..8dcc44463213 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.core + +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Expect +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.verify + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) +class MultiDisplayStatusBarStarterTest : SysuiTestCase() { + @get:Rule val expect: Expect = Expect.create() + + private val kosmos = + testKosmos().also { + it.statusBarOrchestratorFactory = it.fakeStatusBarOrchestratorFactory + it.statusBarInitializerStore = it.fakeStatusBarInitializerStore + } + private val testScope = kosmos.testScope + private val fakeDisplayRepository = kosmos.displayRepository + private val fakeOrchestratorFactory = kosmos.fakeStatusBarOrchestratorFactory + private val fakeInitializerStore = kosmos.fakeStatusBarInitializerStore + + // Lazy, so that @EnableFlags is set before initializer is instantiated. + private val underTest by lazy { kosmos.multiDisplayStatusBarStarter } + + @Test + fun start_startsInitializersForCurrentDisplays() = + testScope.runTest { + fakeDisplayRepository.addDisplay(displayId = 1) + fakeDisplayRepository.addDisplay(displayId = 2) + + underTest.start() + runCurrent() + + expect + .that(fakeInitializerStore.forDisplay(displayId = 1).startedByCoreStartable) + .isTrue() + expect + .that(fakeInitializerStore.forDisplay(displayId = 2).startedByCoreStartable) + .isTrue() + } + + @Test + fun start_startsOrchestratorForCurrentDisplays() = + testScope.runTest { + fakeDisplayRepository.addDisplay(displayId = 1) + fakeDisplayRepository.addDisplay(displayId = 2) + + underTest.start() + runCurrent() + + verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 1)!!).start() + verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 2)!!).start() + } + + @Test + fun displayAdded_orchestratorForNewDisplayIsStarted() = + testScope.runTest { + underTest.start() + runCurrent() + + fakeDisplayRepository.addDisplay(displayId = 3) + runCurrent() + + verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 3)!!).start() + } + + @Test + fun displayAdded_initializerForNewDisplayIsStarted() = + testScope.runTest { + underTest.start() + runCurrent() + + fakeDisplayRepository.addDisplay(displayId = 3) + runCurrent() + + expect + .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable) + .isTrue() + } + + @Test + fun displayAddedDuringStart_initializerForNewDisplayIsStarted() = + testScope.runTest { + underTest.start() + + fakeDisplayRepository.addDisplay(displayId = 3) + runCurrent() + + expect + .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable) + .isTrue() + } + + @Test + fun displayAddedDuringStart_orchestratorForNewDisplayIsStarted() = + testScope.runTest { + underTest.start() + + fakeDisplayRepository.addDisplay(displayId = 3) + runCurrent() + + expect + .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable) + .isTrue() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt index f64387c95e3d..e312d00f3dc2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt @@ -20,12 +20,15 @@ import android.app.FragmentManager import android.app.FragmentTransaction import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags +import android.view.ViewGroup import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.fragments.FragmentHostManager import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent +import com.android.systemui.statusbar.pipeline.shared.ui.composable.StatusBarRootFactory import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.google.common.truth.Truth.assertThat @@ -35,6 +38,8 @@ import org.junit.Before import org.junit.runner.RunWith import org.mockito.Mockito.mock import org.mockito.kotlin.any +import org.mockito.kotlin.never +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @SmallTest @@ -42,27 +47,30 @@ import org.mockito.kotlin.whenever class StatusBarInitializerTest : SysuiTestCase() { private val windowController = mock(StatusBarWindowController::class.java) private val windowControllerStore = mock(StatusBarWindowControllerStore::class.java) + private val transaction = mock(FragmentTransaction::class.java) + private val fragmentManager = mock(FragmentManager::class.java) + private val fragmentHostManager = mock(FragmentHostManager::class.java) + private val backgroundView = mock(ViewGroup::class.java) @Before fun setup() { // TODO(b/364360986) this will go away once the fragment is deprecated. Hence, there is no // need right now for moving this to kosmos - val transaction = mock(FragmentTransaction::class.java) - val fragmentManager = mock(FragmentManager::class.java) - val fragmentHostManager = mock(FragmentHostManager::class.java) whenever(fragmentHostManager.addTagListener(any(), any())).thenReturn(fragmentHostManager) whenever(fragmentHostManager.fragmentManager).thenReturn(fragmentManager) whenever(fragmentManager.beginTransaction()).thenReturn(transaction) whenever(transaction.replace(any(), any(), any())).thenReturn(transaction) whenever(windowControllerStore.defaultDisplay).thenReturn(windowController) whenever(windowController.fragmentHostManager).thenReturn(fragmentHostManager) + whenever(windowController.backgroundView).thenReturn(backgroundView) } val underTest = StatusBarInitializerImpl( - displayId = context.displayId, - statusBarWindowControllerStore = windowControllerStore, + statusBarWindowController = windowController, collapsedStatusBarFragmentProvider = { mock(CollapsedStatusBarFragment::class.java) }, + statusBarRootFactory = mock(StatusBarRootFactory::class.java), + componentFactory = mock(HomeStatusBarComponent.Factory::class.java), creationListeners = setOf(), ) @@ -80,6 +88,15 @@ class StatusBarInitializerTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + fun simpleFragment_flagEnabled_doesNotCreateFragment() { + underTest.start() + + verify(fragmentManager, never()).beginTransaction() + verify(transaction, never()).replace(any(), any(), any()) + } + + @Test @DisableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT) fun flagOff_doesNotInitializeViaCoreStartable() { underTest.start() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt index bb3fb1e71f78..ab8e878ab309 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt @@ -38,13 +38,10 @@ import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT_TRANSPARENT import com.android.systemui.statusbar.data.model.StatusBarMode.OPAQUE import com.android.systemui.statusbar.data.model.StatusBarMode.TRANSPARENT -import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository -import com.android.systemui.statusbar.phone.mockPhoneStatusBarTransitions -import com.android.systemui.statusbar.phone.mockPhoneStatusBarViewController +import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository import com.android.systemui.statusbar.window.data.model.StatusBarWindowState -import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStateRepositoryStore -import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore -import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore +import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository +import com.android.systemui.statusbar.window.fakeStatusBarWindowController import com.android.systemui.testKosmos import com.android.wm.shell.bubbles.bubbles import com.google.common.truth.Truth.assertThat @@ -60,25 +57,20 @@ import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) class StatusBarOrchestratorTest : SysuiTestCase() { - private val kosmos = - testKosmos().also { - it.testDispatcher = it.unconfinedTestDispatcher - it.statusBarWindowStateRepositoryStore = it.fakeStatusBarWindowStateRepositoryStore - } + private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher } private val testScope = kosmos.testScope - private val statusBarViewController = kosmos.mockPhoneStatusBarViewController - private val statusBarWindowControllerStore = kosmos.fakeStatusBarWindowControllerStore - private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository - private val pluginDependencyProvider = kosmos.mockPluginDependencyProvider - private val notificationShadeWindowViewController = + private val fakeStatusBarModePerDisplayRepository = kosmos.fakeStatusBarModePerDisplayRepository + private val mockPluginDependencyProvider = kosmos.mockPluginDependencyProvider + private val mockNotificationShadeWindowViewController = kosmos.mockNotificationShadeWindowViewController - private val shadeSurface = kosmos.mockShadeSurface - private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository - private val fakeStatusBarWindowStateRepositoryStore = - kosmos.fakeStatusBarWindowStateRepositoryStore + private val mockShadeSurface = kosmos.mockShadeSurface + private val fakeBouncerRepository = kosmos.fakeKeyguardBouncerRepository + private val fakeStatusBarWindowStatePerDisplayRepository = + kosmos.fakeStatusBarWindowStatePerDisplayRepository private val fakePowerRepository = kosmos.fakePowerRepository - private val mockPhoneStatusBarTransitions = kosmos.mockPhoneStatusBarTransitions private val mockBubbles = kosmos.bubbles + private val fakeStatusBarWindowController = kosmos.fakeStatusBarWindowController + private val fakeStatusBarInitializer = kosmos.fakeStatusBarInitializer private val orchestrator = kosmos.statusBarOrchestrator @@ -86,30 +78,31 @@ class StatusBarOrchestratorTest : SysuiTestCase() { fun start_setsUpPluginDependencies() { orchestrator.start() - verify(pluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java) - verify(pluginDependencyProvider).allowPluginDependency(StatusBarStateController::class.java) + verify(mockPluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java) + verify(mockPluginDependencyProvider) + .allowPluginDependency(StatusBarStateController::class.java) } @Test fun start_attachesWindow() { orchestrator.start() - assertThat(statusBarWindowControllerStore.defaultDisplay.isAttached).isTrue() + assertThat(fakeStatusBarWindowController.isAttached).isTrue() } @Test fun start_setsStatusBarControllerOnShade() { orchestrator.start() - verify(notificationShadeWindowViewController) - .setStatusBarViewController(statusBarViewController) + verify(mockNotificationShadeWindowViewController) + .setStatusBarViewController(fakeStatusBarInitializer.statusBarViewController) } @Test fun start_updatesShadeExpansion() { orchestrator.start() - verify(shadeSurface).updateExpansionAndVisibility() + verify(mockShadeSurface).updateExpansionAndVisibility() } @Test @@ -117,9 +110,9 @@ class StatusBarOrchestratorTest : SysuiTestCase() { testScope.runTest { orchestrator.start() - bouncerRepository.setPrimaryShow(isShowing = true) + fakeBouncerRepository.setPrimaryShow(isShowing = true) - verify(statusBarViewController) + verify(fakeStatusBarInitializer.statusBarViewController) .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) } @@ -128,9 +121,9 @@ class StatusBarOrchestratorTest : SysuiTestCase() { testScope.runTest { orchestrator.start() - bouncerRepository.setPrimaryShow(isShowing = false) + fakeBouncerRepository.setPrimaryShow(isShowing = false) - verify(statusBarViewController) + verify(fakeStatusBarInitializer.statusBarViewController) .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) } @@ -141,7 +134,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions).finishAnimations() + verify(fakeStatusBarInitializer.statusBarTransitions).finishAnimations() } @Test @@ -151,7 +144,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions, never()).finishAnimations() + verify(fakeStatusBarInitializer.statusBarTransitions, never()).finishAnimations() } @Test @@ -208,7 +201,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true) } @@ -222,19 +215,19 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true) setStatusBarMode(OPAQUE) - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(OPAQUE.toTransitionModeInt(), /* animate= */ true) setStatusBarMode(LIGHTS_OUT) - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(LIGHTS_OUT.toTransitionModeInt(), /* animate= */ true) setStatusBarMode(LIGHTS_OUT_TRANSPARENT) - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(LIGHTS_OUT_TRANSPARENT.toTransitionModeInt(), /* animate= */ true) } @@ -248,7 +241,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false) } @@ -262,7 +255,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false) } @@ -276,7 +269,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false) } @@ -295,7 +288,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { setTransientStatusBar() clearTransientStatusBar() - verify(mockPhoneStatusBarTransitions, times(1)) + verify(fakeStatusBarInitializer.statusBarTransitions, times(1)) .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true) } @@ -318,18 +311,18 @@ class StatusBarOrchestratorTest : SysuiTestCase() { } private fun setTransientStatusBar() { - statusBarModeRepository.defaultDisplay.showTransient() + fakeStatusBarModePerDisplayRepository.showTransient() } private fun clearTransientStatusBar() { - statusBarModeRepository.defaultDisplay.clearTransient() + fakeStatusBarModePerDisplayRepository.clearTransient() } private fun setStatusBarWindowState(state: StatusBarWindowState) { - fakeStatusBarWindowStateRepositoryStore.defaultDisplay.setWindowState(state) + fakeStatusBarWindowStatePerDisplayRepository.setWindowState(state) } private fun setStatusBarMode(statusBarMode: StatusBarMode) { - statusBarModeRepository.defaultDisplay.statusBarMode.value = statusBarMode + fakeStatusBarModePerDisplayRepository.statusBarMode.value = statusBarMode } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt index 48ae7a2aa260..feda0c65bd7f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.phone.BoundsPair import com.android.systemui.statusbar.phone.LetterboxAppearance import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator import com.android.systemui.statusbar.phone.StatusBarBoundsProvider -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel @@ -62,8 +62,8 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { private val commandQueue = mock<CommandQueue>() private val letterboxAppearanceCalculator = mock<LetterboxAppearanceCalculator>() private val statusBarBoundsProvider = mock<StatusBarBoundsProvider>() - private val statusBarFragmentComponent = - mock<StatusBarFragmentComponent>().also { + private val homeStatusBarComponent = + mock<HomeStatusBarComponent>().also { whenever(it.boundsProvider).thenReturn(statusBarBoundsProvider) } private val ongoingCallRepository = kosmos.ongoingCallRepository @@ -78,7 +78,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { ) .apply { this.start() - this.onStatusBarViewInitialized(statusBarFragmentComponent) + this.onStatusBarViewInitialized(homeStatusBarComponent) } private val commandQueueCallback: CommandQueue.Callbacks @@ -235,9 +235,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.isInFullscreenMode) - onSystemBarAttributesChanged( - requestedVisibleTypes = WindowInsets.Type.statusBars(), - ) + onSystemBarAttributesChanged(requestedVisibleTypes = WindowInsets.Type.statusBars()) assertThat(latest).isFalse() } @@ -247,9 +245,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.isInFullscreenMode) - onSystemBarAttributesChanged( - requestedVisibleTypes = WindowInsets.Type.navigationBars(), - ) + onSystemBarAttributesChanged(requestedVisibleTypes = WindowInsets.Type.navigationBars()) assertThat(latest).isTrue() } @@ -259,9 +255,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.isInFullscreenMode) - onSystemBarAttributesChanged( - requestedVisibleTypes = WindowInsets.Type.navigationBars(), - ) + onSystemBarAttributesChanged(requestedVisibleTypes = WindowInsets.Type.navigationBars()) assertThat(latest).isTrue() onSystemBarAttributesChanged( @@ -347,7 +341,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { val startingLetterboxAppearance = LetterboxAppearance( APPEARANCE_LIGHT_STATUS_BARS, - listOf(AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, Rect(0, 0, 1, 1))) + listOf(AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, Rect(0, 0, 1, 1))), ) whenever( letterboxAppearanceCalculator.getLetterboxAppearance( @@ -371,7 +365,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { val newLetterboxAppearance = LetterboxAppearance( APPEARANCE_LOW_PROFILE_BARS, - listOf(AppearanceRegion(APPEARANCE_LOW_PROFILE_BARS, Rect(10, 20, 30, 40))) + listOf(AppearanceRegion(APPEARANCE_LOW_PROFILE_BARS, Rect(10, 20, 30, 40))), ) whenever( letterboxAppearanceCalculator.getLetterboxAppearance( @@ -398,9 +392,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { val latest by collectLastValue(underTest.statusBarAppearance) ongoingCallRepository.setOngoingCallState(inCallModel(startTimeMs = 34)) - onSystemBarAttributesChanged( - requestedVisibleTypes = WindowInsets.Type.navigationBars(), - ) + onSystemBarAttributesChanged(requestedVisibleTypes = WindowInsets.Type.navigationBars()) assertThat(latest!!.mode).isEqualTo(StatusBarMode.SEMI_TRANSPARENT) } @@ -438,9 +430,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { fun statusBarMode_transientShown_semiTransparent() = testScope.runTest { val latest by collectLastValue(underTest.statusBarAppearance) - onSystemBarAttributesChanged( - appearance = APPEARANCE_OPAQUE_STATUS_BARS, - ) + onSystemBarAttributesChanged(appearance = APPEARANCE_OPAQUE_STATUS_BARS) underTest.showTransient() @@ -453,7 +443,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { val latest by collectLastValue(underTest.statusBarAppearance) onSystemBarAttributesChanged( - appearance = APPEARANCE_LOW_PROFILE_BARS or APPEARANCE_OPAQUE_STATUS_BARS, + appearance = APPEARANCE_LOW_PROFILE_BARS or APPEARANCE_OPAQUE_STATUS_BARS ) assertThat(latest!!.mode).isEqualTo(StatusBarMode.LIGHTS_OUT) @@ -464,9 +454,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.statusBarAppearance) - onSystemBarAttributesChanged( - appearance = APPEARANCE_LOW_PROFILE_BARS, - ) + onSystemBarAttributesChanged(appearance = APPEARANCE_LOW_PROFILE_BARS) assertThat(latest!!.mode).isEqualTo(StatusBarMode.LIGHTS_OUT_TRANSPARENT) } @@ -476,9 +464,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.statusBarAppearance) - onSystemBarAttributesChanged( - appearance = APPEARANCE_OPAQUE_STATUS_BARS, - ) + onSystemBarAttributesChanged(appearance = APPEARANCE_OPAQUE_STATUS_BARS) assertThat(latest!!.mode).isEqualTo(StatusBarMode.OPAQUE) } @@ -488,9 +474,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.statusBarAppearance) - onSystemBarAttributesChanged( - appearance = APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS, - ) + onSystemBarAttributesChanged(appearance = APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS) assertThat(latest!!.mode).isEqualTo(StatusBarMode.SEMI_TRANSPARENT) } @@ -500,9 +484,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.statusBarAppearance) - onSystemBarAttributesChanged( - appearance = 0, - ) + onSystemBarAttributesChanged(appearance = 0) assertThat(latest!!.mode).isEqualTo(StatusBarMode.TRANSPARENT) } @@ -540,7 +522,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { LetterboxDetails( /* letterboxInnerBounds= */ Rect(0, 0, 10, 10), /* letterboxFullBounds= */ Rect(0, 0, 20, 20), - /* appAppearance= */ 0 + /* appAppearance= */ 0, ) ) private val REGIONS_FROM_LETTERBOX_CALCULATOR = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt deleted file mode 100644 index 4d5ea92aa0e6..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -@file:OptIn(ExperimentalCoroutinesApi::class) - -package com.android.systemui.statusbar.notification.collection.coordinator - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.statusbar.SysuiStatusBarStateController -import com.android.systemui.statusbar.notification.collection.NotifPipeline -import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.withArgCaptor -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.clearInvocations -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidJUnit4::class) -class DreamCoordinatorTest : SysuiTestCase() { - @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController - @Mock private lateinit var notifPipeline: NotifPipeline - @Mock private lateinit var filterListener: Pluggable.PluggableListener<NotifFilter> - - private val keyguardRepository = FakeKeyguardRepository() - private var fakeEntry: NotificationEntry = NotificationEntryBuilder().build() - val testDispatcher = UnconfinedTestDispatcher() - val testScope = TestScope(testDispatcher) - - private lateinit var filter: NotifFilter - private lateinit var statusBarListener: StatusBarStateController.StateListener - private lateinit var dreamCoordinator: DreamCoordinator - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) - - // Build the coordinator - dreamCoordinator = - DreamCoordinator( - statusBarStateController, - testScope.backgroundScope, - keyguardRepository - ) - - // Attach the pipeline and capture the listeners/filters that it registers - dreamCoordinator.attach(notifPipeline) - - filter = withArgCaptor { verify(notifPipeline).addPreGroupFilter(capture()) } - filter.setInvalidationListener(filterListener) - - statusBarListener = withArgCaptor { - verify(statusBarStateController).addCallback(capture()) - } - } - - @Test - fun hideNotifications_whenDreamingAndOnKeyguard() = - testScope.runTest { - // GIVEN we are on keyguard and not dreaming - keyguardRepository.setKeyguardShowing(true) - keyguardRepository.setIsActiveDreamLockscreenHosted(false) - runCurrent() - - // THEN notifications are not filtered out - verifyPipelinesNotInvalidated() - assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isFalse() - - // WHEN dreaming starts and the active dream is hosted in lockscreen - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - runCurrent() - - // THEN pipeline is notified and notifications should all be filtered out - verifyPipelinesInvalidated() - assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isTrue() - } - - @Test - fun showNotifications_whenDreamingAndNotOnKeyguard() = - testScope.runTest { - // GIVEN we are on the keyguard and active dream is hosted in lockscreen - keyguardRepository.setKeyguardShowing(true) - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - runCurrent() - - // THEN pipeline is notified and notifications are all filtered out - verifyPipelinesInvalidated() - clearPipelineInvocations() - assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isTrue() - - // WHEN we are no longer on the keyguard - statusBarListener.onStateChanged(StatusBarState.SHADE) - - // THEN pipeline is notified and notifications are not filtered out - verifyPipelinesInvalidated() - assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isFalse() - } - - @Test - fun showNotifications_whenOnKeyguardAndNotDreaming() = - testScope.runTest { - // GIVEN we are on the keyguard and active dream is hosted in lockscreen - keyguardRepository.setKeyguardShowing(true) - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - runCurrent() - - // THEN pipeline is notified and notifications are all filtered out - verifyPipelinesInvalidated() - clearPipelineInvocations() - assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isTrue() - - // WHEN the lockscreen hosted dream stops - keyguardRepository.setIsActiveDreamLockscreenHosted(false) - runCurrent() - - // THEN pipeline is notified and notifications are not filtered out - verifyPipelinesInvalidated() - assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isFalse() - } - - private fun verifyPipelinesInvalidated() { - verify(filterListener).onPluggableInvalidated(eq(filter), any()) - } - - private fun verifyPipelinesNotInvalidated() { - verify(filterListener, never()).onPluggableInvalidated(eq(filter), any()) - } - - private fun clearPipelineInvocations() { - clearInvocations(filterListener) - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java index d04d6fc5f593..0947cd5aaabb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java @@ -45,12 +45,12 @@ import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.service.notification.StatusBarNotification; -import android.testing.UiThreadTest; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java index 22f1e4604bbd..6435e8203d3d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java @@ -24,9 +24,9 @@ import static org.mockito.Mockito.mock; import android.provider.Settings; import android.testing.TestableResources; -import android.testing.UiThreadTest; import android.util.KeyValueListParser; +import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index 95db95cd288b..789701f5e4b0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -13,6 +13,8 @@ package com.android.systemui.statusbar.notification.stack; +import static com.android.systemui.Flags.FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -36,6 +38,7 @@ import static org.mockito.Mockito.when; import android.animation.Animator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.os.Handler; +import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.service.notification.StatusBarNotification; import android.testing.TestableLooper; @@ -52,6 +55,7 @@ import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; +import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization; @@ -85,6 +89,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { private NotificationMenuRowPlugin mMenuRow; private Handler mHandler; private ExpandableNotificationRow mNotificationRow; + private NotificationShelf mShelf; private Runnable mFalsingCheck; private final FeatureFlags mFeatureFlags = new FakeFeatureFlags(); @@ -111,6 +116,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { mEvent = mock(MotionEvent.class); mMenuRow = mock(NotificationMenuRowPlugin.class); mNotificationRow = mock(ExpandableNotificationRow.class); + mShelf = mock(NotificationShelf.class); mHandler = mock(Handler.class); mFalsingCheck = mock(Runnable.class); } @@ -665,6 +671,54 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { } @Test + @EnableFlags(FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF) + public void testIsTouchInView_notificationShelf_flagEnabled() { + doReturn(500).when(mShelf).getWidth(); + doReturn(FAKE_ROW_WIDTH).when(mShelf).getActualWidth(); + doReturn(FAKE_ROW_HEIGHT).when(mShelf).getHeight(); + doReturn(FAKE_ROW_HEIGHT).when(mShelf).getActualHeight(); + + Answer answer = (Answer) invocation -> { + int[] arr = invocation.getArgument(0); + arr[0] = 0; + arr[1] = 0; + return null; + }; + + doReturn(5f).when(mEvent).getRawX(); + doReturn(10f).when(mEvent).getRawY(); + doAnswer(answer).when(mShelf).getLocationOnScreen(any()); + assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf)); + + doReturn(50f).when(mEvent).getRawX(); + assertFalse("Touch is not within the view", mSwipeHelper.isTouchInView(mEvent, mShelf)); + } + + @Test + @DisableFlags(FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF) + public void testIsTouchInView_notificationShelf_flagDisabled() { + doReturn(500).when(mShelf).getWidth(); + doReturn(FAKE_ROW_WIDTH).when(mShelf).getActualWidth(); + doReturn(FAKE_ROW_HEIGHT).when(mShelf).getHeight(); + doReturn(FAKE_ROW_HEIGHT).when(mShelf).getActualHeight(); + + Answer answer = (Answer) invocation -> { + int[] arr = invocation.getArgument(0); + arr[0] = 0; + arr[1] = 0; + return null; + }; + + doReturn(5f).when(mEvent).getRawX(); + doReturn(10f).when(mEvent).getRawY(); + doAnswer(answer).when(mShelf).getLocationOnScreen(any()); + assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf)); + + doReturn(50f).when(mEvent).getRawX(); + assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf)); + } + + @Test public void testContentAlphaRemainsUnchangedWhenNotificationIsNotDismissible() { doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth(); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt index 8d678ef00b4a..bf144729dea3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt @@ -21,7 +21,6 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest -import com.android.app.tracing.coroutines.flow.map import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.DisableSceneContainer @@ -51,6 +50,7 @@ import com.android.systemui.util.ui.isAnimating import com.android.systemui.util.ui.value import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt index c804fc6990ae..ba5fb7f8df00 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt @@ -98,7 +98,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl) @@ -135,7 +135,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl) @@ -164,8 +164,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { val chipWidth = 30 val dotWidth = 10 val isRtl = false - val contentRect = - Rect(/* left = */ 0, /* top = */ 10, /* right = */ 1000, /* bottom = */ 100) + val contentRect = Rect(/* left= */ 0, /* top= */ 10, /* right= */ 1000, /* bottom= */ 100) val chipBounds = getPrivacyChipBoundingRectForInsets(contentRect, dotWidth, chipWidth, isRtl) @@ -207,7 +206,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -228,7 +227,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -251,7 +250,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -263,7 +262,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { minLeftPadding, 0, screenBounds.height() - dcBounds.height() - dotWidth, - sbHeightLandscape + sbHeightLandscape, ) bounds = @@ -278,7 +277,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -320,7 +319,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -331,7 +330,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { protectionBounds.bottom, 0, screenBounds.height() - minRightPadding, - sbHeightLandscape + sbHeightLandscape, ) bounds = @@ -346,7 +345,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -369,7 +368,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -381,7 +380,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { minLeftPadding, 0, screenBounds.height() - protectionBounds.bottom - dotWidth, - sbHeightLandscape + sbHeightLandscape, ) bounds = @@ -396,7 +395,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -415,7 +414,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { left = screenBounds.right - dcWidth, top = 0, right = screenBounds.right, - bottom = dcHeight + bottom = dcHeight, ) val dcBoundsLandscape = Rect(left = 0, top = 0, right = dcHeight, bottom = dcWidth) val dcBoundsSeascape = @@ -423,14 +422,14 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { left = screenBounds.right - dcHeight, top = screenBounds.bottom - dcWidth, right = screenBounds.right - dcHeight, - bottom = screenBounds.bottom - dcWidth + bottom = screenBounds.bottom - dcWidth, ) val dcBoundsUpsideDown = Rect( left = 0, top = screenBounds.bottom - dcHeight, right = dcWidth, - bottom = screenBounds.bottom - dcHeight + bottom = screenBounds.bottom - dcHeight, ) val minLeftPadding = 20 val minRightPadding = 20 @@ -448,7 +447,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { left = minLeftPadding, top = 0, right = dcBoundsPortrait.left - dotWidth, - bottom = sbHeightPortrait + bottom = sbHeightPortrait, ) var bounds = @@ -463,7 +462,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -475,7 +474,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { left = dcBoundsLandscape.height(), top = 0, right = screenBounds.height() - minRightPadding, - bottom = sbHeightLandscape + bottom = sbHeightLandscape, ) bounds = @@ -490,7 +489,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -502,7 +501,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { left = minLeftPadding, top = 0, right = screenBounds.width() - minRightPadding, - bottom = sbHeightPortrait + bottom = sbHeightPortrait, ) bounds = @@ -517,7 +516,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -529,7 +528,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { left = minLeftPadding, top = 0, right = screenBounds.height() - minRightPadding, - bottom = sbHeightLandscape + bottom = sbHeightLandscape, ) bounds = @@ -544,7 +543,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -584,7 +583,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -595,7 +594,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { protectionBounds.bottom, 0, screenBounds.height() - minRightPadding, - sbHeightLandscape + sbHeightLandscape, ) bounds = @@ -610,7 +609,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -633,7 +632,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -645,7 +644,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { minLeftPadding, 0, screenBounds.height() - protectionBounds.bottom - dotWidth, - sbHeightLandscape + sbHeightLandscape, ) bounds = @@ -660,7 +659,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -682,7 +681,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl = false, dotWidth = 10, bottomAlignedMargin = BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight = 15 + statusBarContentHeight = 15, ) assertThat(bounds.top).isEqualTo(0) @@ -704,7 +703,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl = false, dotWidth = 10, bottomAlignedMargin = 5, - statusBarContentHeight = 15 + statusBarContentHeight = 15, ) // Content in the status bar is centered vertically. To achieve the bottom margin we want, @@ -756,7 +755,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -777,7 +776,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -798,7 +797,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -809,7 +808,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { minLeftPadding, 0, screenBounds.height() - dcBounds.height() - dotWidth, - sbHeightLandscape + sbHeightLandscape, ) bounds = @@ -824,7 +823,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -860,7 +859,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -880,7 +879,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -900,7 +899,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -920,7 +919,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) } @@ -958,7 +957,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { isRtl, dotWidth, BOTTOM_ALIGNED_MARGIN_NONE, - statusBarContentHeight + statusBarContentHeight, ) assertRects(expectedBounds, bounds, currentRotation, targetRotation) @@ -968,12 +967,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { fun testDisplayChanged_returnsUpdatedInsets() { // GIVEN: get insets on the first display and switch to the second display val provider = - StatusBarContentInsetsProvider( + StatusBarContentInsetsProviderImpl( contextMock, configurationController, mock<DumpManager>(), mock<CommandRegistry>(), - mock<SysUICutoutProvider>() + mock<SysUICutoutProvider>(), ) configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160)) @@ -993,12 +992,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { // GIVEN: get insets on the first display, switch to the second display, // get insets and switch back val provider = - StatusBarContentInsetsProvider( + StatusBarContentInsetsProviderImpl( contextMock, configurationController, mock<DumpManager>(), mock<CommandRegistry>(), - mock<SysUICutoutProvider>() + mock<SysUICutoutProvider>(), ) configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160)) @@ -1024,12 +1023,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100) configurationController.onConfigurationChanged(configuration) val provider = - StatusBarContentInsetsProvider( + StatusBarContentInsetsProviderImpl( contextMock, configurationController, mock<DumpManager>(), mock<CommandRegistry>(), - mock<SysUICutoutProvider>() + mock<SysUICutoutProvider>(), ) val listener = object : StatusBarContentInsetsChangedListener { @@ -1053,12 +1052,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { fun onDensityOrFontScaleChanged_listenerNotified() { configuration.densityDpi = 12 val provider = - StatusBarContentInsetsProvider( + StatusBarContentInsetsProviderImpl( contextMock, configurationController, mock<DumpManager>(), mock<CommandRegistry>(), - mock<SysUICutoutProvider>() + mock<SysUICutoutProvider>(), ) val listener = object : StatusBarContentInsetsChangedListener { @@ -1081,12 +1080,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { @Test fun onThemeChanged_listenerNotified() { val provider = - StatusBarContentInsetsProvider( + StatusBarContentInsetsProviderImpl( contextMock, configurationController, mock<DumpManager>(), mock<CommandRegistry>(), - mock<SysUICutoutProvider>() + mock<SysUICutoutProvider>(), ) val listener = object : StatusBarContentInsetsChangedListener { @@ -1108,13 +1107,13 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { expected: Rect, actual: Rect, @Rotation currentRotation: Int, - @Rotation targetRotation: Int + @Rotation targetRotation: Int, ) { assertTrue( "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" + " targetRotation=${RotationUtils.toString(targetRotation)}" + " expected=$expected actual=$actual", - expected.equals(actual) + expected.equals(actual), ) } @@ -1126,7 +1125,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { left: Rect = Rect(), top: Rect = Rect(), right: Rect = Rect(), - bottom: Rect = Rect() + bottom: Rect = Rect(), ) { whenever(dc.boundingRects) .thenReturn(listOf(left, top, right, bottom).filter { !it.isEmpty }) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 1d74331e429b..94753f7e5203 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -34,7 +34,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,9 +42,7 @@ import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; -import android.service.trust.TrustAgentService; import android.testing.TestableLooper; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; @@ -67,7 +64,6 @@ import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; @@ -580,22 +576,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - @DisableSceneContainer - @DisableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void testShowAlternateBouncer_unlockingWithBiometricAllowed() { - // GIVEN will show alternate bouncer - when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false); - when(mAlternateBouncerInteractor.show()).thenReturn(true); - - // WHEN showGenericBouncer is called - mStatusBarKeyguardViewManager.showBouncer(true); - - // THEN alt auth bouncer is shown - verify(mAlternateBouncerInteractor).show(); - verify(mPrimaryBouncerInteractor, never()).show(anyBoolean()); - } - - @Test public void testUpdateResources_delegatesToBouncer() { mStatusBarKeyguardViewManager.updateResources(); @@ -841,145 +821,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void handleDispatchTouchEvent_alternateBouncerViewFlagEnabled() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // THEN the touch is not acted upon - verify(mCallback, never()).onTouch(any()); - } - - @Test - @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void onInterceptTouch_alternateBouncerViewFlagEnabled() { - // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // THEN the touch is not intercepted - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - } - - @Test - public void handleDispatchTouchEvent_alternateBouncerNotVisible() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - - // THEN handleDispatchTouchEvent doesn't use the touches - assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - - // THEN the touch is not acted upon - verify(mCallback, never()).onTouch(any()); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void handleDispatchTouchEvent_shouldInterceptTouchAndHandleTouch() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // GIVEN all touches are NOT the udfps overlay - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent - // to its child views (handleDispatchTouchEvent returns true) - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - - // THEN the touch is acted upon once for each dispatchTOuchEvent call - verify(mCallback, times(3)).onTouch(any()); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void handleDispatchTouchEvent_shouldInterceptTouchButNotHandleTouch() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // GIVEN all touches are within the udfps overlay - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(true); - - // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent - // to its child views (handleDispatchTouchEvent returns true) - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - - // THEN the touch is NOT acted upon at the moment - verify(mCallback, never()).onTouch(any()); - } - - @Test - @DisableSceneContainer - public void shouldInterceptTouch_alternateBouncerNotVisible() { - // GIVEN the alternate bouncer is not visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - - // THEN no motion events are intercepted - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void shouldInterceptTouch_alternateBouncerVisible() { - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // THEN all motion events are intercepted - assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - } - - @Test public void alternateBouncerToShowPrimaryBouncer_updatesScrimControllerOnce() { // GIVEN the alternate bouncer has shown and calls to hide() will result in successfully // hiding it @@ -997,106 +838,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void alternateBouncerOnTouch_actionDownThenUp_noMinTimeShown_noHideAltBouncer() { - reset(mAlternateBouncerInteractor); - - // GIVEN the alternate bouncer has shown for a minimum amount of time - when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(false); - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // WHEN ACTION_DOWN and ACTION_UP touch event comes - boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)); - when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true); - boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)); - - // THEN the touches are handled (doesn't let touches through to underlying views) - assertTrue(touchHandledDown); - assertTrue(touchHandledUp); - - // THEN alternate bouncer does NOT attempt to hide since min showing time wasn't met - verify(mAlternateBouncerInteractor, never()).hide(); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void alternateBouncerOnTouch_actionDownThenUp_handlesTouch_hidesAltBouncer() { - reset(mAlternateBouncerInteractor); - - // GIVEN the alternate bouncer has shown for a minimum amount of time - when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true); - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // WHEN ACTION_DOWN and ACTION_UP touch event comes - boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)); - when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true); - boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)); - - // THEN the touches are handled - assertTrue(touchHandledDown); - assertTrue(touchHandledUp); - - // THEN alternate bouncer attempts to hide - verify(mAlternateBouncerInteractor).hide(); - } - - @Test - @DisableSceneContainer - public void alternateBouncerOnTouch_actionUp_doesNotHideAlternateBouncer() { - reset(mAlternateBouncerInteractor); - - // GIVEN the alternate bouncer has shown for a minimum amount of time - when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true); - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // WHEN only ACTION_UP touch event comes - mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)); - - // THEN the alternateBouncer doesn't hide - verify(mAlternateBouncerInteractor, never()).hide(); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void onTrustChanged_hideAlternateBouncerAndClearMessageArea() { - // GIVEN keyguard update monitor callback is registered - verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture()); - - reset(mKeyguardUpdateMonitor); - reset(mKeyguardMessageAreaController); - - // GIVEN alternate bouncer state = not visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - - // WHEN the device is trusted by active unlock - mKeyguardUpdateMonitorCallback.getValue().onTrustGrantedForCurrentUser( - true, - true, - new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD - | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE), - null - ); - - // THEN the false visibility state is propagated to the keyguardUpdateMonitor - verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false)); - - // THEN message area visibility updated to FALSE with empty message - verify(mKeyguardMessageAreaController).setIsVisible(eq(false)); - verify(mKeyguardMessageAreaController).setMessage(eq("")); - } - - @Test - @DisableSceneContainer @DisableFlags(Flags.FLAG_SIM_PIN_RACE_CONDITION_ON_RESTART) public void testShowBouncerOrKeyguard_needsFullScreen() { when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewBinder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt index 2ee928fa6d17..cdc7aa2dea2a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt @@ -17,21 +17,21 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel import android.view.View -import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder +import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener /** * A fake view binder that can be used from Java tests. * * Since Java tests can't run tests within test scopes, we need to bypass the flows from - * [CollapsedStatusBarViewModel] and just trigger the listener directly. + * [HomeStatusBarViewModel] and just trigger the listener directly. */ -class FakeCollapsedStatusBarViewBinder : CollapsedStatusBarViewBinder { +class FakeHomeStatusBarViewBinder : HomeStatusBarViewBinder { var listener: StatusBarVisibilityChangeListener? = null override fun bind( view: View, - viewModel: CollapsedStatusBarViewModel, + viewModel: HomeStatusBarViewModel, listener: StatusBarVisibilityChangeListener, ) { this.listener = listener diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt index cc90c1167ef1..02c1540d3d8b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel { +class FakeHomeStatusBarViewModel : HomeStatusBarViewModel { private val areNotificationLightsOut = MutableStateFlow(false) override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false) @@ -39,7 +39,7 @@ class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel { override val isClockVisible = MutableStateFlow( - CollapsedStatusBarViewModel.VisibilityModel( + HomeStatusBarViewModel.VisibilityModel( visibility = View.GONE, shouldAnimateChange = false, ) @@ -47,7 +47,7 @@ class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel { override val isNotificationIconContainerVisible = MutableStateFlow( - CollapsedStatusBarViewModel.VisibilityModel( + HomeStatusBarViewModel.VisibilityModel( visibility = View.GONE, shouldAnimateChange = false, ) @@ -55,7 +55,7 @@ class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel { override val isSystemInfoVisible = MutableStateFlow( - CollapsedStatusBarViewModel.VisibilityModel( + HomeStatusBarViewModel.VisibilityModel( visibility = View.GONE, shouldAnimateChange = false, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt index bd857807851c..b3a73d82122f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel +import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP import android.app.StatusBarManager.DISABLE2_NONE import android.app.StatusBarManager.DISABLE_CLOCK import android.app.StatusBarManager.DISABLE_NONE @@ -33,6 +34,7 @@ import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -75,7 +77,7 @@ import org.junit.runner.RunWith @SmallTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) -class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { +class HomeStatusBarViewModelImplTest : SysuiTestCase() { private val kosmos = Kosmos().also { it.testCase = this @@ -89,13 +91,13 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository private val disableFlagsRepository = kosmos.fakeDisableFlagsRepository - private lateinit var underTest: CollapsedStatusBarViewModel + private lateinit var underTest: HomeStatusBarViewModel @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) // Initialize here because some flags are checked when this class is constructed - underTest = kosmos.collapsedStatusBarViewModel + underTest = kosmos.homeStatusBarViewModel } @Test @@ -746,6 +748,45 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) } + @Test + @DisableSceneContainer + fun secureCameraActive_sceneFlagOff_noStatusBarViewsShown() = + testScope.runTest { + val clockVisible by collectLastValue(underTest.isClockVisible) + val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) + val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + + // Secure camera is an occluding activity + keyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.OCCLUDED, + testScope = this, + ) + kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) + + assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) + assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) + assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) + } + + @Test + @EnableSceneContainer + fun secureCameraActive_sceneFlagOn_noStatusBarViewsShown() = + testScope.runTest { + val clockVisible by collectLastValue(underTest.isClockVisible) + val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) + val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + + kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) + // Secure camera is an occluding activity + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, taskInfo = null) + kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) + + assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) + assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) + assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) + } + private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = ActiveNotificationsStore.Builder() .apply { notifications.forEach(::addIndividualNotif) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt index 4a53a7a74bad..fd8958166a6c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.policy import android.app.AutomaticZenRule import android.app.NotificationManager import android.net.Uri +import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -42,6 +43,7 @@ import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) @SmallTest +@EnableFlags(android.app.Flags.FLAG_MODES_UI) class ZenModesCleanupStartableTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImplTest.kt new file mode 100644 index 000000000000..6e66287c1683 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImplTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.window + +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays +import com.android.systemui.statusbar.policy.statusBarConfigurationController +import com.android.systemui.testKosmos +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.never +import org.mockito.kotlin.verify + +@SmallTest +@RunWith(AndroidJUnit4::class) +class StatusBarWindowControllerImplTest : SysuiTestCase() { + + private val kosmos = + testKosmos().also { it.statusBarWindowViewInflater = it.fakeStatusBarWindowViewInflater } + + private val underTest = kosmos.statusBarWindowControllerImpl + private val fakeStatusBarWindowViewInflater = kosmos.fakeStatusBarWindowViewInflater + private val statusBarConfigurationController = kosmos.statusBarConfigurationController + + @Test + @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun attach_connectedDisplaysFlagEnabled_setsConfigControllerOnWindowView() { + val windowView = fakeStatusBarWindowViewInflater.inflatedMockViews.first() + + underTest.attach() + + verify(windowView).setStatusBarConfigurationController(statusBarConfigurationController) + } + + @Test + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun attach_connectedDisplaysFlagDisabled_doesNotSetConfigControllerOnWindowView() { + val mockWindowView = fakeStatusBarWindowViewInflater.inflatedMockViews.first() + + underTest.attach() + + verify(mockWindowView, never()).setStatusBarConfigurationController(any()) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowViewTest.kt new file mode 100644 index 000000000000..9917f9996532 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowViewTest.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.window + +import android.content.res.Configuration +import android.view.LayoutInflater +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify + +@SmallTest +@RunWith(AndroidJUnit4::class) +class StatusBarWindowViewTest : SysuiTestCase() { + + private val underTest = + LayoutInflater.from(context).inflate(R.layout.super_status_bar, /* root= */ null) + as StatusBarWindowView + + @Test + fun onConfigurationChanged_configurationControllerSet_forwardsCall() { + val configuration = Configuration() + val configurationController = mock<StatusBarConfigurationController>() + underTest.setStatusBarConfigurationController(configurationController) + + underTest.onConfigurationChanged(configuration) + + verify(configurationController).onConfigurationChanged(configuration) + } + + @Test + fun onConfigurationChanged_configurationControllerNotSet_doesNotCrash() { + val configuration = Configuration() + + underTest.onConfigurationChanged(configuration) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt index 40c3f221e2df..29e9ba752b36 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt @@ -61,6 +61,26 @@ class BackGestureRecognizerTest : SysuiTestCase() { } @Test + fun triggersProgressRelativeToDistance() { + assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f) + assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE / 2, expectedProgress = 0.5f) + assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE, expectedProgress = 1f) + assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE, expectedProgress = 1f) + } + + private fun assertProgressWhileMovingFingers(deltaX: Float, expectedProgress: Float) { + assertStateAfterEvents( + events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) }, + expectedState = InProgress(progress = expectedProgress), + ) + } + + @Test + fun triggeredProgressIsNoBiggerThanOne() { + assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE * 2, expectedProgress = 1f) + } + + @Test fun doesntTriggerGestureFinished_onGestureDistanceTooShort() { assertStateAfterEvents( events = ThreeFingerGesture.swipeLeft(distancePx = SWIPE_DISTANCE / 2), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt index 8406d3b99bac..ff0cec5e06e9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt @@ -104,7 +104,8 @@ class EasterEggGestureTest : SysuiTestCase() { } private fun assertStateAfterTwoFingerGesture(gesturePath: List<Point>, wasTriggered: Boolean) { - val events = TwoFingerGesture.createEvents { gesturePath.forEach { (x, y) -> move(x, y) } } + val events = + TwoFingerGesture.eventsForFullGesture { gesturePath.forEach { (x, y) -> move(x, y) } } assertStateAfterEvents(events = events, wasTriggered = wasTriggered) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt index 043b77577978..7d3ed92cecc6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt @@ -56,6 +56,27 @@ class HomeGestureRecognizerTest : SysuiTestCase() { } @Test + fun triggersProgressRelativeToDistance() { + assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f) + assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f) + } + + private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) { + assertStateAfterEvents( + events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) }, + expectedState = InProgress(progress = expectedProgress), + ) + } + + @Test + fun triggeredProgressIsBetweenZeroAndOne() { + // going in the wrong direction + assertProgressWhileMovingFingers(deltaY = SWIPE_DISTANCE / 2, expectedProgress = 0f) + // going further than required distance + assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f) + } + + @Test fun doesntTriggerGestureFinished_onGestureDistanceTooShort() { assertStateAfterEvents( events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt index 7095a91a4e5d..c5c0d59ea48b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt @@ -77,11 +77,32 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() { fun triggersGestureProgressForThreeFingerGestureStarted() { assertStateAfterEvents( events = ThreeFingerGesture.startEvents(x = 0f, y = 0f), - expectedState = InProgress(), + expectedState = InProgress(progress = 0f), ) } @Test + fun triggersProgressRelativeToDistance() { + assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f) + assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f) + } + + private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) { + assertStateAfterEvents( + events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) }, + expectedState = InProgress(progress = expectedProgress), + ) + } + + @Test + fun triggeredProgressIsBetweenZeroAndOne() { + // going in the wrong direction + assertProgressWhileMovingFingers(deltaY = SWIPE_DISTANCE / 2, expectedProgress = 0f) + // going further than required distance + assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f) + } + + @Test fun doesntTriggerGestureFinished_onGestureDistanceTooShort() { assertStateAfterEvents( events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt index 296d4dce8ce4..42fe1e5d6bec 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt @@ -25,11 +25,23 @@ import android.view.MotionEvent.ACTION_UP import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_X import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_Y -/** - * Interface for gesture builders which support creating list of [MotionEvent] for common swipe - * gestures. For simple usage see swipe* methods or use [createEvents] for more specific scenarios. - */ -interface MultiFingerGesture { +/** Given gesture move events can build list of [MotionEvent]s included in that gesture */ +interface GestureEventsBuilder { + /** + * Creates full gesture including provided move events. This means returned events include DOWN, + * MOVE and UP. Note that move event's x and y is always relative to the starting one. + */ + fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> + + /** + * Creates partial gesture including provided move events. This means returned events include + * DOWN and MOVE. Note that move event's x and y is always relative to the starting one. + */ + fun eventsForGestureInProgress(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> +} + +/** Support creating list of [MotionEvent] for common swipe gestures. */ +interface MultiFingerGesture : GestureEventsBuilder { companion object { const val SWIPE_DISTANCE = 100f @@ -37,27 +49,41 @@ interface MultiFingerGesture { const val DEFAULT_Y = 500f } - fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = -distancePx) } - - fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = distancePx) } + fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { + move(deltaY = -distancePx) + } - fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = distancePx) } + fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { + move(deltaY = distancePx) + } - fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = -distancePx) } + fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { + move(deltaX = distancePx) + } - /** - * Creates gesture with provided move events. Note that move event's x and y is always relative - * to the starting one - */ - fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> + fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { + move(deltaX = -distancePx) + } } object ThreeFingerGesture : MultiFingerGesture { - override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> { - return touchpadGesture( + + private val moveEventsBuilder = MoveEventsBuilder(::threeFingerEvent) + + override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> { + return buildGesture( + startEvents = { x, y -> startEvents(x, y) }, + moveEvents = moveEventsBuilder.getEvents(moveEvents), + endEvents = { x, y -> endEvents(x, y) }, + ) + } + + override fun eventsForGestureInProgress( + moveEvents: MoveEventsBuilder.() -> Unit + ): List<MotionEvent> { + return buildGesture( startEvents = { x, y -> startEvents(x, y) }, - moveEvents = GestureBuilder(::threeFingerEvent).apply { moveEvents() }.events, - endEvents = { x, y -> endEvents(x, y) } + moveEvents = moveEventsBuilder.getEvents(moveEvents), ) } @@ -65,7 +91,7 @@ object ThreeFingerGesture : MultiFingerGesture { return listOf( threeFingerEvent(ACTION_DOWN, x, y), threeFingerEvent(ACTION_POINTER_DOWN, x, y), - threeFingerEvent(ACTION_POINTER_DOWN, x, y) + threeFingerEvent(ACTION_POINTER_DOWN, x, y), ) } @@ -73,32 +99,43 @@ object ThreeFingerGesture : MultiFingerGesture { return listOf( threeFingerEvent(ACTION_POINTER_UP, x, y), threeFingerEvent(ACTION_POINTER_UP, x, y), - threeFingerEvent(ACTION_UP, x, y) + threeFingerEvent(ACTION_UP, x, y), ) } private fun threeFingerEvent( action: Int, x: Float = DEFAULT_X, - y: Float = DEFAULT_Y + y: Float = DEFAULT_Y, ): MotionEvent { return touchpadEvent( action = action, x = x, y = y, classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE, - axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f) + axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f), ) } } object FourFingerGesture : MultiFingerGesture { - override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> { - return touchpadGesture( + private val moveEventsBuilder = MoveEventsBuilder(::fourFingerEvent) + + override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> { + return buildGesture( + startEvents = { x, y -> startEvents(x, y) }, + moveEvents = moveEventsBuilder.getEvents(moveEvents), + endEvents = { x, y -> endEvents(x, y) }, + ) + } + + override fun eventsForGestureInProgress( + moveEvents: MoveEventsBuilder.() -> Unit + ): List<MotionEvent> { + return buildGesture( startEvents = { x, y -> startEvents(x, y) }, - moveEvents = GestureBuilder(::fourFingerEvent).apply { moveEvents() }.events, - endEvents = { x, y -> endEvents(x, y) } + moveEvents = moveEventsBuilder.getEvents(moveEvents), ) } @@ -107,7 +144,7 @@ object FourFingerGesture : MultiFingerGesture { fourFingerEvent(ACTION_DOWN, x, y), fourFingerEvent(ACTION_POINTER_DOWN, x, y), fourFingerEvent(ACTION_POINTER_DOWN, x, y), - fourFingerEvent(ACTION_POINTER_DOWN, x, y) + fourFingerEvent(ACTION_POINTER_DOWN, x, y), ) } @@ -116,61 +153,74 @@ object FourFingerGesture : MultiFingerGesture { fourFingerEvent(ACTION_POINTER_UP, x, y), fourFingerEvent(ACTION_POINTER_UP, x, y), fourFingerEvent(ACTION_POINTER_UP, x, y), - fourFingerEvent(ACTION_UP, x, y) + fourFingerEvent(ACTION_UP, x, y), ) } private fun fourFingerEvent( action: Int, x: Float = DEFAULT_X, - y: Float = DEFAULT_Y + y: Float = DEFAULT_Y, ): MotionEvent { return touchpadEvent( action = action, x = x, y = y, classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE, - axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f) + axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f), ) } } object TwoFingerGesture : MultiFingerGesture { - override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> { - return touchpadGesture( - startEvents = { x, y -> listOf(twoFingerEvent(ACTION_DOWN, x, y)) }, - moveEvents = GestureBuilder(::twoFingerEvent).apply { moveEvents() }.events, - endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) } + private val moveEventsBuilder = MoveEventsBuilder(::twoFingerEvent) + + override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> { + return buildGesture( + startEvents = { x, y -> startEvents(x, y) }, + moveEvents = moveEventsBuilder.getEvents(moveEvents), + endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) }, + ) + } + + override fun eventsForGestureInProgress( + moveEvents: MoveEventsBuilder.() -> Unit + ): List<MotionEvent> { + return buildGesture( + startEvents = { x, y -> startEvents(x, y) }, + moveEvents = moveEventsBuilder.getEvents(moveEvents), ) } + private fun startEvents(x: Float, y: Float) = listOf(twoFingerEvent(ACTION_DOWN, x, y)) + private fun twoFingerEvent( action: Int, x: Float = DEFAULT_X, - y: Float = DEFAULT_Y + y: Float = DEFAULT_Y, ): MotionEvent { return touchpadEvent( action = action, x = x, y = y, classification = MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE, - axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f) + axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f), ) } } -private fun touchpadGesture( +private fun buildGesture( startEvents: (Float, Float) -> List<MotionEvent>, moveEvents: List<MotionEvent>, - endEvents: (Float, Float) -> List<MotionEvent> + endEvents: (Float, Float) -> List<MotionEvent> = { _, _ -> emptyList() }, ): List<MotionEvent> { val lastX = moveEvents.last().x val lastY = moveEvents.last().y return startEvents(DEFAULT_X, DEFAULT_Y) + moveEvents + endEvents(lastX, lastY) } -class GestureBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) { +class MoveEventsBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) { val events = mutableListOf<MotionEvent>() @@ -178,3 +228,11 @@ class GestureBuilder internal constructor(val eventBuilder: (Int, Float, Float) events.add(eventBuilder(ACTION_MOVE, DEFAULT_X + deltaX, DEFAULT_Y + deltaY)) } } + +private fun MoveEventsBuilder.getEvents( + moveEvents: MoveEventsBuilder.() -> Unit +): List<MotionEvent> { + events.clear() + this.moveEvents() + return events +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt index 13ebb42531b8..64136775b4eb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt @@ -54,7 +54,28 @@ class TouchpadGestureBuilderTest : SysuiTestCase() { ACTION_MOVE, ACTION_POINTER_UP, ACTION_POINTER_UP, - ACTION_UP + ACTION_UP, + ) + .inOrder() + } + + @Test + fun threeFingerGestureInProgressProducesCorrectEvents() { + val events = + ThreeFingerGesture.eventsForGestureInProgress { + move(deltaX = 10f) + move(deltaX = 20f) + } + + val actions = events.map { it.actionMasked } + assertWithMessage("Events have expected action type") + .that(actions) + .containsExactly( + ACTION_DOWN, + ACTION_POINTER_DOWN, + ACTION_POINTER_DOWN, + ACTION_MOVE, + ACTION_MOVE, ) .inOrder() } @@ -80,7 +101,7 @@ class TouchpadGestureBuilderTest : SysuiTestCase() { ACTION_POINTER_UP, ACTION_POINTER_UP, ACTION_POINTER_UP, - ACTION_UP + ACTION_UP, ) .inOrder() } @@ -109,7 +130,7 @@ class TouchpadGestureBuilderTest : SysuiTestCase() { @Test fun gestureBuilderProducesCorrectEventCoordinates() { val events = - ThreeFingerGesture.createEvents { + ThreeFingerGesture.eventsForFullGesture { move(deltaX = 50f) move(deltaX = 100f) } @@ -127,7 +148,7 @@ class TouchpadGestureBuilderTest : SysuiTestCase() { // up events DEFAULT_X + 100f to DEFAULT_Y, DEFAULT_X + 100f to DEFAULT_Y, - DEFAULT_X + 100f to DEFAULT_Y + DEFAULT_X + 100f to DEFAULT_Y, ) .inOrder() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt index a867eb38b44c..c302b40fc4d7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt @@ -85,7 +85,7 @@ class TouchpadGestureHandlerTest : SysuiTestCase() { } private fun backGestureEvents(): List<MotionEvent> { - return ThreeFingerGesture.createEvents { + return ThreeFingerGesture.eventsForFullGesture { move(deltaX = SWIPE_DISTANCE / 4) move(deltaX = SWIPE_DISTANCE / 2) move(deltaX = SWIPE_DISTANCE) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt index 5d850d82d806..f1015394d7b1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt @@ -18,15 +18,20 @@ package com.android.systemui.unfold import android.content.Context import android.content.res.Resources +import android.hardware.devicestate.DeviceStateManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor +import com.android.systemui.defaultDeviceState +import com.android.systemui.deviceStateManager import com.android.systemui.display.data.repository.DeviceStateRepository import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState +import com.android.systemui.foldedDeviceStateList import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.kosmos.Kosmos import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.power.shared.model.WakeSleepReason @@ -39,10 +44,10 @@ import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABL import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor +import com.android.systemui.unfoldedDeviceState import com.android.systemui.util.animation.data.repository.AnimationStatusRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture -import com.android.systemui.util.mockito.mock import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.Optional @@ -63,6 +68,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import org.mockito.kotlin.mock @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) @@ -78,8 +84,14 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { private val animationStatusRepository = mock<AnimationStatusRepository>() private val keyguardInteractor = mock<KeyguardInteractor>() private val displaySwitchLatencyLogger = mock<DisplaySwitchLatencyLogger>() + private val kosmos = Kosmos() + private val deviceStateManager = kosmos.deviceStateManager + private val closedDeviceState = kosmos.foldedDeviceStateList.first() + private val openDeviceState = kosmos.unfoldedDeviceState + private val defaultDeviceState = kosmos.defaultDeviceState + private val nonEmptyClosedDeviceStatesArray: IntArray = + IntArray(2) { closedDeviceState.identifier } - private val nonEmptyClosedDeviceStatesArray: IntArray = IntArray(2) { 0 } private val testDispatcher: TestDispatcher = StandardTestDispatcher() private val testScope: TestScope = TestScope(testDispatcher) private val isAsleep = MutableStateFlow(false) @@ -108,6 +120,10 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { fun setup() { MockitoAnnotations.initMocks(this) whenever(mockContext.resources).thenReturn(resources) + whenever(mockContext.getSystemService(DeviceStateManager::class.java)) + .thenReturn(deviceStateManager) + whenever(deviceStateManager.supportedDeviceStates) + .thenReturn(listOf(closedDeviceState, openDeviceState)) whenever(resources.getIntArray(R.array.config_foldedDeviceStates)) .thenReturn(nonEmptyClosedDeviceStatesArray) whenever(foldStateRepository.state).thenReturn(deviceState) @@ -128,7 +144,8 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { testDispatcher.asExecutor(), testScope.backgroundScope, displaySwitchLatencyLogger, - systemClock + systemClock, + deviceStateManager ) } @@ -182,7 +199,8 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { testDispatcher.asExecutor(), testScope.backgroundScope, displaySwitchLatencyLogger, - systemClock + systemClock, + deviceStateManager ) areAnimationEnabled.emit(true) @@ -321,6 +339,8 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { deviceState.emit(DeviceState.UNFOLDED) whenever(resources.getIntArray(R.array.config_foldedDeviceStates)) .thenReturn(IntArray(0)) + whenever(deviceStateManager.supportedDeviceStates) + .thenReturn(listOf(defaultDeviceState)) displaySwitchLatencyTracker.start() deviceState.emit(DeviceState.HALF_FOLDED) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt index 1e5929daf9c0..a1122c3cbcd2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt @@ -23,9 +23,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.systemui.SysuiTestCase +import com.android.systemui.foldedDeviceStateList +import com.android.systemui.halfFoldedDeviceState import com.android.systemui.keyguard.ScreenLifecycle +import com.android.systemui.kosmos.Kosmos import com.android.systemui.unfold.util.FoldableDeviceStates import com.android.systemui.unfold.util.FoldableTestUtils +import com.android.systemui.unfoldedDeviceState import com.android.systemui.util.mockito.any import java.util.Optional import org.junit.Before @@ -38,6 +42,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @SmallTest @@ -62,6 +67,13 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) + whenever(deviceStateManager.supportedDeviceStates).thenReturn( + listOf( + Kosmos().foldedDeviceStateList[0], + Kosmos().unfoldedDeviceState + ) + ) + unfoldLatencyTracker = UnfoldLatencyTracker( latencyTracker, @@ -73,6 +85,7 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() { screenLifecycle ) .apply { init() } + deviceStates = FoldableTestUtils.findDeviceStates(context) verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture()) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt index e4a1c2680658..9a9ec137a075 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt @@ -18,34 +18,53 @@ package com.android.systemui.unfold.util import android.content.Context import android.hardware.devicestate.DeviceState +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY +import android.hardware.devicestate.DeviceStateManager +import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags import org.junit.Assume.assumeTrue object FoldableTestUtils { - /** Finds device state for folded and unfolded. */ fun findDeviceStates(context: Context): FoldableDeviceStates { - // TODO(b/325474477): Migrate clients to updated DeviceStateManager API's - val foldedDeviceStates: IntArray = context.resources.getIntArray( - com.android.internal.R.array.config_foldedDeviceStates) - assumeTrue("Test should be launched on a foldable device", - foldedDeviceStates.isNotEmpty()) - - val folded = getDeviceState( - identifier = foldedDeviceStates.maxOrNull()!! - ) - val unfolded = getDeviceState( - identifier = folded.identifier + 1 - ) - return FoldableDeviceStates(folded = folded, unfolded = unfolded) + if (DeviceStateManagerFlags.deviceStatePropertyMigration()) { + val deviceStateManager = context.getSystemService(DeviceStateManager::class.java) + val deviceStateList = deviceStateManager.supportedDeviceStates + + val folded = + deviceStateList.firstOrNull { state -> + state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY) + } + val unfolded = + deviceStateList.firstOrNull { state -> + state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY) + } + + assumeTrue( + "Test should only be ran on a foldable device", + (folded != null) && (unfolded != null) + ) + + return FoldableDeviceStates(folded = folded!!, unfolded = unfolded!!) + } else { + val foldedDeviceStates: IntArray = + context.resources.getIntArray( + com.android.internal.R.array.config_foldedDeviceStates + ) + assumeTrue( + "Test should be launched on a foldable device", + foldedDeviceStates.isNotEmpty() + ) + + val folded = getDeviceState(identifier = foldedDeviceStates.maxOrNull()!!) + val unfolded = getDeviceState(identifier = folded.identifier + 1) + return FoldableDeviceStates(folded = folded, unfolded = unfolded) + } } private fun getDeviceState(identifier: Int): DeviceState { - return DeviceState( - DeviceState.Configuration.Builder( - identifier, "" /* name */ - ).build() - ) + return DeviceState(DeviceState.Configuration.Builder(identifier, "" /* name */).build()) } } -data class FoldableDeviceStates(val folded: DeviceState, val unfolded: DeviceState)
\ No newline at end of file +data class FoldableDeviceStates(val folded: DeviceState, val unfolded: DeviceState) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt new file mode 100644 index 000000000000..b4ba41a5c524 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2017 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.util + +import android.hardware.devicestate.DeviceState +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN +import android.hardware.devicestate.feature.flags.Flags +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.testing.TestableResources +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.deviceStateManager +import com.android.systemui.kosmos.Kosmos +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +class UtilsTest : SysuiTestCase() { + + private val kosmos = Kosmos() + private val deviceStateManager = kosmos.deviceStateManager + private lateinit var testableResources: TestableResources + + @Before + fun setUp() { + testableResources = context.orCreateTestableResources + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION) + fun isFoldableReturnsFalse_overlayConfigurationValues() { + testableResources.addOverride( + com.android.internal.R.array.config_foldedDeviceStates, + intArrayOf() // empty array <=> device is not foldable + ) + whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE)) + assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager)) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION) + fun isFoldableReturnsFalse_deviceStateManager() { + testableResources.addOverride( + com.android.internal.R.array.config_foldedDeviceStates, + intArrayOf() // empty array <=> device is not foldable + ) + whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE)) + assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager)) + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION) + fun isFoldableReturnsTrue_overlayConfigurationValues() { + testableResources.addOverride( + com.android.internal.R.array.config_foldedDeviceStates, + intArrayOf(FOLDED_DEVICE_STATE.identifier) + ) + whenever(deviceStateManager.supportedDeviceStates) + .thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE)) + assertTrue(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager)) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION) + fun isFoldableReturnsTrue_deviceStateManager() { + testableResources.addOverride( + com.android.internal.R.array.config_foldedDeviceStates, + intArrayOf(FOLDED_DEVICE_STATE.identifier) + ) + whenever(deviceStateManager.supportedDeviceStates) + .thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE)) + assertTrue(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager)) + } + + companion object { + private val DEFAULT_DEVICE_STATE = + DeviceState(DeviceState.Configuration.Builder(0 /* identifier */, "DEFAULT").build()) + private val FOLDED_DEVICE_STATE = + DeviceState( + DeviceState.Configuration.Builder(1 /* identifier */, "FOLDED") + .setSystemProperties( + setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED) + ) + .build() + ) + private val UNFOLDED_DEVICE_STATE = + DeviceState( + DeviceState.Configuration.Builder(2 /* identifier */, "UNFOLDED") + .setSystemProperties( + setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN) + ) + .build() + ) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt index b8f581574848..a4b3916b7a55 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt @@ -35,7 +35,7 @@ class DrawableSizeTest : SysuiTestCase() { val drawable = BitmapDrawable( resources, - Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888), ) val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300) assertThat(result).isSameInstanceAs(drawable) @@ -48,7 +48,7 @@ class DrawableSizeTest : SysuiTestCase() { val drawable = BitmapDrawable( resources, - Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888), ) val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75) @@ -64,4 +64,31 @@ class DrawableSizeTest : SysuiTestCase() { val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) assertThat(result).isSameInstanceAs(drawable) } + + @Test + fun testDownscaleToSize_layerDrawable_allLayersSameType_resized() { + val drawable = + resources.getDrawable( + com.android.systemui.tests.R.drawable.layer_drawable_all_same_type, + resources.newTheme(), + ) + + val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) + + assertThat(result).isNotSameInstanceAs(drawable) + } + + /** Regression test for b/244282477. */ + @Test + fun testDownscaleToSize_layerDrawable_layersAreDifferentTypes_unchanged() { + val drawable = + resources.getDrawable( + com.android.systemui.tests.R.drawable.layer_drawable_different_types, + resources.newTheme(), + ) + + val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) + + assertThat(result).isSameInstanceAs(drawable) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt new file mode 100644 index 000000000000..76a1269a1fe9 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.volume.domain.interactor + +import android.media.AudioManager +import android.platform.test.annotations.EnableFlags +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.settingslib.volume.shared.model.AudioStream +import com.android.systemui.Flags +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.android.systemui.volume.data.repository.audioRepository +import com.android.systemui.volume.data.repository.audioSystemRepository +import com.android.systemui.volume.panel.component.volume.domain.model.SliderType +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class AudioSlidersInteractorTest : SysuiTestCase() { + + private val kosmos = testKosmos() + + private lateinit var underTest: AudioSlidersInteractor + + @Before + fun setUp() = + with(kosmos) { + audioRepository.setMode(AudioManager.MODE_NORMAL) + underTest = audioSlidersInteractor + } + + @Test + fun shouldAddAllStreams_notInCall() = + with(kosmos) { + testScope.runTest { + val sliders by collectLastValue(underTest.volumePanelSliders) + runCurrent() + + assertThat(sliders).isEqualTo( + mutableListOf( + AudioManager.STREAM_MUSIC, + AudioManager.STREAM_VOICE_CALL, + AudioManager.STREAM_RING, + AudioManager.STREAM_NOTIFICATION, + AudioManager.STREAM_ALARM + ).map { SliderType.Stream(AudioStream(it)) }) + } + } + + @Test + fun shouldAddAllStreams_inCall() = + with(kosmos) { + testScope.runTest { + audioRepository.setMode(AudioManager.MODE_IN_CALL) + + val sliders by collectLastValue(underTest.volumePanelSliders) + runCurrent() + + // Call stream is before music stream while in call. + assertThat(sliders).isEqualTo( + mutableListOf( + AudioManager.STREAM_VOICE_CALL, + AudioManager.STREAM_MUSIC, + AudioManager.STREAM_RING, + AudioManager.STREAM_NOTIFICATION, + AudioManager.STREAM_ALARM + ).map { SliderType.Stream(AudioStream(it)) }) + } + } + + + @Test + @EnableFlags(Flags.FLAG_ONLY_SHOW_MEDIA_STREAM_SLIDER_IN_SINGLE_VOLUME_MODE) + fun shouldAddMusicStreamOnly_singleVolumeMode() = + with(kosmos) { + testScope.runTest { + audioSystemRepository.setIsSingleVolume(true) + + val sliders by collectLastValue(underTest.volumePanelSliders) + runCurrent() + + assertThat(sliders).isEqualTo( + mutableListOf(SliderType.Stream(AudioStream(AudioManager.STREAM_MUSIC)))) + } + } +} diff --git a/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml index 627b92b8a779..3c1668405909 100644 --- a/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml +++ b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml @@ -21,11 +21,8 @@ android:color="?android:attr/colorControlHighlight"> <item> <shape android:shape="rectangle"> - <solid android:color="@android:color/transparent"/> + <solid android:color="?androidprv:attr/materialColorPrimaryContainer"/> <corners android:radius="@dimen/hearing_devices_preset_spinner_background_radius"/> - <stroke - android:width="1dp" - android:color="?androidprv:attr/textColorTertiary" /> </shape> </item> </ripple> diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml index 4a7bef9f48b9..80692f9481b7 100644 --- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml +++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml @@ -72,7 +72,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@id/device_function_barrier" - app:layout_constraintBottom_toTopOf="@id/related_tools_scroll" android:drawableStart="@drawable/ic_add" android:drawablePadding="20dp" android:drawableTint="?android:attr/textColorPrimary" @@ -92,24 +91,16 @@ app:barrierDirection="bottom" app:constraint_referenced_ids="device_function_barrier, pair_new_device_button" /> - <HorizontalScrollView - android:id="@+id/related_tools_scroll" + <LinearLayout + android:id="@+id/related_tools_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin" android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin" android:layout_marginTop="@dimen/hearing_devices_layout_margin" - android:scrollbars="none" - android:fillViewport="true" + android:orientation="horizontal" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/preset_spinner"> - <LinearLayout - android:id="@+id/related_tools_container" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal"> - </LinearLayout> - </HorizontalScrollView> + app:layout_constraintTop_toBottomOf="@id/device_barrier" /> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/hearing_tool_item.xml b/packages/SystemUI/res/layout/hearing_tool_item.xml index ff2fbe070e0f..f5baf2aaf6dc 100644 --- a/packages/SystemUI/res/layout/hearing_tool_item.xml +++ b/packages/SystemUI/res/layout/hearing_tool_item.xml @@ -17,8 +17,8 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tool_view" - android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_width="0dp" + android:layout_height="wrap_content" android:orientation="vertical" android:gravity="top|center_horizontal" android:focusable="true" @@ -26,8 +26,10 @@ android:layout_weight="1"> <FrameLayout android:id="@+id/icon_frame" - android:layout_width="@dimen/hearing_devices_tool_icon_frame_width" - android:layout_height="@dimen/hearing_devices_tool_icon_frame_height" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="20dp" + android:paddingBottom="20dp" android:background="@drawable/qs_hearing_devices_related_tools_background" android:focusable="false" > <ImageView diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml index 690a89a044b7..d0a1ce8ae629 100644 --- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml @@ -45,31 +45,26 @@ android:tint="?android:attr/colorPrimary" /> - <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text] will ever - be shown at one time. --> + <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text, + ongoing_activity_chip_short_time_delta] will ever be shown at one time. --> + + <!-- Shows a timer, like 00:01. --> <com.android.systemui.statusbar.chips.ui.view.ChipChronometer android:id="@+id/ongoing_activity_chip_time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:gravity="center|start" - android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding" - android:textAppearance="@android:style/TextAppearance.Material.Small" - android:fontFamily="@*android:string/config_headlineFontFamily" - android:textColor="?android:attr/colorPrimary" + style="@style/StatusBar.Chip.Text" /> - <!-- Used to show generic text in the chip instead of a timer. --> + <!-- Shows generic text. --> <TextView android:id="@+id/ongoing_activity_chip_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:gravity="center|start" - android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding" - android:textAppearance="@android:style/TextAppearance.Material.Small" - android:fontFamily="@*android:string/config_headlineFontFamily" - android:textColor="?android:attr/colorPrimary" + style="@style/StatusBar.Chip.Text" + android:visibility="gone" + /> + + <!-- Shows a time delta in short form, like "15min" or "1hr". --> + <android.widget.DateTimeView + android:id="@+id/ongoing_activity_chip_short_time_delta" + style="@style/StatusBar.Chip.Text" android:visibility="gone" /> diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index e21466671363..77fbb642f664 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -128,12 +128,6 @@ <include layout="@layout/dock_info_bottom_area_overlay" /> - <com.android.keyguard.LockIconView - android:id="@+id/lock_icon_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - </com.android.keyguard.LockIconView> - <include layout="@layout/keyguard_bottom_area" android:visibility="gone" /> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 22d34eb7b115..fbb07bed4b50 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -58,22 +58,22 @@ android:layout_height="match_parent" android:visibility="invisible" /> - <!-- Shared container for the notification stack. Can be positioned by either - the keyguard_root_view or notification_panel --> - <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer - android:id="@+id/shared_notification_container" + <!-- Root for all keyguard content. It was previously located within the shade. --> + <com.android.systemui.keyguard.ui.view.KeyguardRootView + android:id="@id/keyguard_root_view" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" - android:clipToPadding="false" /> - <!-- Root for all keyguard content. It was previously located within the shade. --> - <com.android.systemui.keyguard.ui.view.KeyguardRootView - android:id="@id/keyguard_root_view" + <!-- Shared container for the notification stack. Can be positioned by either + the keyguard_root_view or notification_panel --> + <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer + android:id="@+id/shared_notification_container" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" + android:clipToPadding="false" /> <include layout="@layout/brightness_mirror_container" /> diff --git a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml b/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml deleted file mode 100644 index 4799f8c5b668..000000000000 --- a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?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. - --> -<com.android.systemui.biometrics.UdfpsFpmEmptyView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/udfps_animation_view" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <!-- The layout height/width are placeholders, which will be overwritten by - FingerprintSensorPropertiesInternal. --> - <View - android:id="@+id/udfps_enroll_accessibility_view" - android:layout_gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:contentDescription="@string/accessibility_fingerprint_label"/> -</com.android.systemui.biometrics.UdfpsFpmEmptyView> diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml deleted file mode 100644 index 257d238f5c54..000000000000 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 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. - --> -<com.android.systemui.biometrics.UdfpsView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res-auto" - android:id="@+id/udfps_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - systemui:sensorTouchAreaCoefficient="1.0" - android:contentDescription="@string/accessibility_fingerprint_label"> - - <ViewStub - android:id="@+id/animation_view" - android:layout_width="match_parent" - android:layout_height="match_parent"/> - -</com.android.systemui.biometrics.UdfpsView> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 62bff957c61e..f00ba9dad6c4 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deel oudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deel tans oudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"voer instellings vir oudiodeling in"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Hierdie toestel se musiek en video’s sal op albei stelle oorfone speel"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Deel jou oudio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> en <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Skakel oor na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Onbeskikbaar omdat luitoon gedemp is"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Onbeskikbaar want Moenie Steur Nie is aan"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Onbeskikbaar want Moenie Steur Nie is aan"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Onbeskikbaar omdat <xliff:g id="MODE">%s</xliff:g> aan is"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Onbeskikbaar"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om te ontdemp."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om op vibreer te stel. Toeganklikheidsdienste kan dalk gedemp wees."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te demp. Toeganklikheidsdienste kan dalk gedemp wees."</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 74e072b3e0e0..9f759fb43a43 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ኦዲዮ አጋራ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ኦዲዮ በማጋራት ላይ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"የድምፅ ማጋሪያ ቅንብሮች አስገባ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"የዚህ መሣሪያ ሙዚቃ እና ቪዲዮዎች በሁለቱም የራስ ላይ ማዳመጫዎች ላይ ይጫወታሉ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ኦዲዮዎን ያጋሩ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> እና <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"ወደ <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ቀይር"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"የጥሪ ድምጽ ስለተዘጋ አይገኝም"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"አትረብሽ ስለበራ አይገኝም"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"አትረብሽ ስለበራ አይገኝም"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ስለበራ አይገኝም"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"አይገኝም"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s። ወደ ንዝረት ለማቀናበር መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 922caeaf558d..c2539a7ec689 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"مشاركة الصوت"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"جارٍ مشاركة الصوت"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"أدخِل إعدادات ميزة \"مشاركة الصوت\""</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"سيتمّ تشغيل الموسيقى والفيديوهات في هذا الجهاز على سماعات الرأس"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"مشاركة صوت جهازك"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"\"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\" و\"<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>\""</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"التبديل إلى \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\""</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"غير متاح بسبب كتم صوت الرنين"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"مستوى الصوت غير متاح بسبب تفعيل وضع \"عدم الإزعاج\""</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"مستوى الصوت غير متاح لأنّ وضع \"عدم الإزعاج\" مفعّل"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"غير متوفِّر لأنّ وضع \"<xliff:g id="MODE">%s</xliff:g>\" مفعَّل"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"غير متوفِّر"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. انقر لإلغاء التجاهل."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. انقر للتعيين على الاهتزاز. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. انقر للتجاهل. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index f353428b2bb7..240d29eaaf28 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"অডিঅ’ শ্বেয়াৰ কৰক"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"অডিঅ’ শ্বেয়াৰ কৰাৰ ছেটিঙলৈ যাওক"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"এই ডিভাইচটোৰ সংগীত আৰু ভিডিঅ’সমূহ দুয়োটা হেডফ’নতে প্লে’ হ’ব"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"আপোনাৰ অডিঅ’ শ্বেয়াৰ কৰক"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> আৰু <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>লৈ সলনি কৰক"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ৰিং মিউট কৰি থোৱাৰ বাবে উপলব্ধ নহয়"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"অসুবিধা নিদিব অন থকাৰ কাৰণে উপলব্ধ নহয়"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"অসুবিধা নিদিব অন থকাৰ কাৰণে উপলব্ধ নহয়"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> অন থকাৰ বাবে উপলব্ধ নহয়"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"উপলব্ধ নহয়"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। আনমিউট কৰিবৰ বাবে টিপক।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। কম্পনৰ বাবে টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট কৰিবলৈ টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 6d2ec46ed831..d3ebd7d6f43c 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audio paylaşın"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio paylaşılır"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"audio paylaşma ayarlarına daxil olun"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Bu cihazın musiqi və videoları hər iki qulaqlıqda oxudulacaq"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Audio paylaşın"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> və <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> cihazına keçin"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Zəng səssiz edildiyi üçün əlçatan deyil"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Narahat Etməyin aktiv olduğu üçün əlçatan deyil"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Narahat Etməyin aktiv olduğu üçün əlçatan deyil"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> yanılı olduğu üçün əlçatan deyil"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Əlçatan deyil"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Səsli etmək üçün tıklayın."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Vibrasiyanı ayarlamaq üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Səssiz etmək üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index b2a983ebc223..a1fc84637a90 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deli zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deli se zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"uđite u podešavanja deljenja zvuka"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzika i videi sa ovog uređaja se reprodukuju na paru slušalica"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Delite zvuk"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> i <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Pređi na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvuk isključen"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je uključen režim Ne uznemiravaj"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupno jer je uključen režim Ne uznemiravaj"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nije dostupno jer je uključeno: <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupno"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da biste uključili zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite da biste podesili na vibraciju. Zvuk usluga pristupačnosti će možda biti isključen."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Zvuk usluga pristupačnosti će možda biti isključen."</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index a0e536b0881f..526a11e0cb8f 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Абагуліць аўдыя"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ідзе абагульванне аўдыя"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"адкрыць налады абагульвання аўдыя"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музыка і відэа на гэтай прыладзе будуць прайгравацца праз абедзве пары навушнікаў"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Абагульвайце аўдыя"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> і <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Пераключыцца на прыладу \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\""</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недаступна, бо выключаны гук выклікаў"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недаступна, бо ўключаны рэжым \"Не турбаваць\""</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Недаступна, бо ўключаны рэжым \"Не турбаваць\""</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недаступна, бо ўключаны рэжым \"<xliff:g id="MODE">%s</xliff:g>\""</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недаступна"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дакраніцеся, каб уключыць гук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Дакраніцеся, каб уключыць вібрацыю. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дакраніцеся, каб адключыць гук. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 1702b8374b99..b843068e85d7 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Споделяне на звука"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Звукът се споделя"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"отваряне на настройките за споделяне на звука"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музиката и видеоклиповете на това устройство ще се възпроизвеждат и на двата чифта слушалки"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Споделяне на звука ви"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> и <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Превключване към <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Не е налице, защото звъненето е спряно"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Не е налице, защото режимът „Не безпокойте“ е вкл."</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Не е налице, защото „Не безпокойте“ е вкл."</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Не е налице, защото режимът „<xliff:g id="MODE">%s</xliff:g>“ е включен"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Не е налице"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Докоснете, за да включите отново звука."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Докоснете, за да зададете вибриране. Възможно е звукът на услугите за достъпност да бъде заглушен."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Докоснете, за да заглушите звука. Възможно е звукът на услугите за достъпност да бъде заглушен."</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index a36fe05c263c..0732387fe337 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"অডিও শেয়ার করুন"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"অডিও শেয়ার করা হচ্ছে"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"অডিও শেয়ার করার সেটিংসে যান"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"এই ডিভাইসের মিউজিক ও ভিডিও, হেডফোনের দুটি পেয়ারেই চলবে"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"আপনার অডিও শেয়ার করুন"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ও <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>-এ পাল্টান"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"চালু আছে"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"চালু আছে • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"বন্ধ আছে"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"সেট করা নেই"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"সেটিংসে গিয়ে ম্যানেজ করুন"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{কোনও মোড চালু নেই}=1{{mode} চালু আছে}one{# মোড চালু আছে}other{# মোড চালু আছে}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"অ্যালার্ম, রিমাইন্ডার, ইভেন্ট, এবং আপনার নির্দিষ্ট করে দেওয়া ব্যক্তিদের কল ছাড়া অন্য কোনও আওয়াজ বা ভাইব্রেশন হবে না। তবে সঙ্গীত, ভিডিও, এবং গেম সহ আপনি যা কিছু চালাবেন তার আওয়াজ শুনতে পাবেন।"</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"রিং মিউট করা হয়েছে বলে উপলভ্য নেই"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\'বিরক্ত করবে না\' মোড চালু থাকার জন্য উপলভ্য নেই"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"\'বিরক্ত করবে না\' মোড চালু থাকার জন্য উপলভ্য নেই"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> চালু হওয়ার জন্য এই সুবিধা উপলভ্য নেই"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"উপলভ্য নেই"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। সশব্দ করতে আলতো চাপুন।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। কম্পন এ সেট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"টাচপ্যাডের জেসচার সম্পর্কে জানুন"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"আপনার কীবোর্ড এবং টাচপ্যাড ব্যবহার করে নেভিগেট করুন"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"টাচপ্যাড জেসচার, কীবোর্ড শর্টকাট এবং আরও অনেক কিছু সম্পর্কে জানুন"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ফিরে যান"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"হোমে যান"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপ দেখুন"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হয়ে গেছে"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ফিরে যান"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"আপনার টাচপ্যাডে তিনটি আঙুল ব্যবহার করে বাঁদিকে বা ডানদিকে সোয়াইপ করুন"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"সাবাস!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"জেসচার ব্যবহার করে কীভাবে ফিরে যাওয়া যায় সেই সম্পর্কে আপনি জেনেছেন।"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"হোমে যান"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"আপনার টাচপ্যাডে তিনটি আঙুলের সাহায্যে উপরের দিকে সোয়াইপ করুন"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"অসাধারণ!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"জেসচার ব্যবহার করে কীভাবে হোমে ফিরে যাওয়া যায় সেই সম্পর্কে আপনি জেনেছেন"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপ দেখুন"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"আপনার টাচপ্যাডে তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করে ধরে রাখুন"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"অসাধারণ!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপের জেসচার দেখা সম্পূর্ণ করেছেন।"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"সব অ্যাপ দেখুন"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপনার কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"দারুণ!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"আপনি \'সব অ্যাপের জেসচার দেখুন\' টিউটোরিয়াল সম্পূর্ণ করেছেন"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীবোর্ড ব্যাকলাইট"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-এর মধ্যে %1$d লেভেল"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"হোম কন্ট্রোল"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 13a933e8964a..b1146f4c482c 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Dijeli zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Dijeljenje zvuka"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ulazak u postavke dijeljenja zvuka"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzika i videozapisi na uređaju će se reproducirati na oba para slušalica"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Dijelite zvuk"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> i <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Prebaci na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno zbog isključenog zvona"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je funkcija Ne ometaj uključena"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupno jer je funkcija Ne ometaj uključena"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nije dostupno jer je način rada <xliff:g id="MODE">%s</xliff:g> uključen"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupno"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da uključite zvukove."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite za postavljanje vibracije. Zvukovi usluga pristupačnosti mogu biti isključeni."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da isključite zvuk. Zvukovi usluga pristupačnosti mogu biti isključeni."</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 0767849dd775..a836b97bfa79 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Comparteix l\'àudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"S\'està compartint l\'àudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"introduir la configuració de compartició d\'àudio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"La música i els vídeos d\'aquest dispositiu es reproduiran en tots dos parells d\'auriculars"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Comparteix l\'àudio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> i <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Canvia a <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string> @@ -578,7 +582,7 @@ <string name="empty_shade_text" msgid="8935967157319717412">"No hi ha cap notificació"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"No hi ha cap notificació nova"</string> <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"La moderació de notificacions està activada"</string> - <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volum i les alertes del dispositiu es redueixen automàticament durant 2 minuts quan reps massa notificacions alhora."</string> + <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volum i les alertes del dispositiu es redueixen automàticament durant 2 minuts com a màxim quan reps massa notificacions alhora."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactiva"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueja per veure notif. anteriors"</string> <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Els teus pares gestionen aquest dispositiu"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible perquè el so està silenciat"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"No disponible perquè No molestis està activat"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"No disponible perquè No molestis està activat"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"No està disponible perquè <xliff:g id="MODE">%s</xliff:g> està activat"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"No disponible"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca per activar el so."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca per activar la vibració. Pot ser que els serveis d\'accessibilitat se silenciïn."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca per silenciar el so. Pot ser que els serveis d\'accessibilitat se silenciïn."</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index c7ec42cea19e..70bdb6277343 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Sdílet zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zvuk se sdílí"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"přejdete do nastavení sdílení zvuku"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Hudba a videa z tohoto zařízení se budou přehrávat na obou párech sluchátek"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Sdílet zvuk"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> a <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Přepnout na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupné, protože vyzvánění je ztlumené"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupné, protože je zapnutý režim Nerušit"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupné – je zapnutý režim Nerušit"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nedostupné, protože je zapnutý režim <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupné"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Klepnutím zapnete zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Klepnutím aktivujete režim vibrací. Služby přístupnosti mohou být ztlumeny."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnete zvuk. Služby přístupnosti mohou být ztlumeny."</string> @@ -934,8 +936,8 @@ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Přidat dlaždici na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozice není platná."</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Karta byla přidána"</string> - <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Karta byla odstraněna"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Dlaždice byla přidána"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Dlaždice byla odstraněna"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Oznámení aplikace <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otevřít nastavení."</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index b5220d6f526f..eeece4c4395b 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Del lyd"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deler lyd"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"angive indstillinger for lyddeling"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Musik og videoer fra denne enhed afspilles på begge par høretelefoner"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Del din lyd"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> og <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Skift til <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ikke muligt, da ringetonen er slået fra"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ikke tilgængelig, fordi Forstyr ikke er aktiveret"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ikke tilgængelig, fordi Forstyr ikke er aktiveret"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ikke tilgængelig, fordi <xliff:g id="MODE">%s</xliff:g> er aktiveret"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ikke tilgængelig"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tryk for at slå lyden til."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tryk for at konfigurere til at vibrere. Tilgængelighedstjenester kan blive deaktiveret."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryk for at slå lyden fra. Lyden i tilgængelighedstjenester kan blive slået fra."</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 9367d40fd67e..95703148499b 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audioinhalte freigeben"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioinhalte werden freigegeben"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"Einstellungen für die Audiofreigabe eingeben"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Musik und Videos auf diesem Gerät werden auf beiden Kopfhörerpaaren abgespielt"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Audioinhalte freigeben"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> und <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Zu <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> wechseln"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"An"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"An • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Aus"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nicht festgelegt"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"In den Einstellungen verwalten"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Keine aktiven Modi}=1{{mode} aktiv}other{# aktiv}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Klingeltöne und die Vibration werden deaktiviert, außer für Weckrufe, Erinnerungen, Termine sowie Anrufe von zuvor von dir festgelegten Personen. Du hörst jedoch weiterhin Sound, wenn du dir Musik anhörst, Videos ansiehst oder Spiele spielst."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nicht verfügbar, da Klingelton stumm"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nicht verfügbar, weil „Bitte nicht stören“ an ist"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nicht verfügbar, weil „Bitte nicht stören“ an"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nicht verfügbar, weil <xliff:g id="MODE">%s</xliff:g> aktiviert ist"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nicht verfügbar"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Zum Aufheben der Stummschaltung tippen."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tippen, um Vibrieren festzulegen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Zum Stummschalten tippen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Informationen zu Touchpad-Gesten"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigation mit Tastatur und Touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Informationen zu Touchpad-Gesten, Tastenkombinationen und mehr"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Zurück"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Zur Startseite"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Letzte Apps aufrufen"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fertig"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zurück"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Wische mit drei Fingern auf dem Touchpad nach links oder rechts"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Sehr gut!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du hast den Schritt für die „Zurück“-Geste abgeschlossen."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Startbildschirm"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Wische an einer beliebigen Stelle auf dem Touchpad mit drei Fingern nach oben"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Gut gemacht!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Du hast den Schritt für die „Startbildschirm“-Touch-Geste abgeschlossen"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Letzte Apps aufrufen"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Wische mit drei Fingern nach oben und halte das Touchpad gedrückt"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Gut gemacht!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du hast das Tutorial für die Touch-Geste zum Aufrufen der zuletzt verwendeten Apps abgeschlossen."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle Apps anzeigen"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Drücke die Aktionstaste auf deiner Tastatur"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Perfekt!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du hast das Tutorial für die Touch-Geste zum Aufrufen aller Apps abgeschlossen"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 18b99aae6723..5551740f67d7 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Κοινή χρήση ήχου"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Κοινή χρήση ήχου σε εξέλιξη"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"είσοδο στις ρυθμίσεις κοινής χρήσης ήχου"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Η μουσική και τα βίντεο αυτής της συσκευής θα αναπαράγονται και στα δύο ζευγάρια ακουστικών"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Κοινή χρήση του ήχου σας"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> και <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Εναλλαγή σε <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Μη διαθέσιμο λόγω σίγασης ήχου κλήσης"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Μη διαθ. επειδή η λειτ. Μην ενοχλείτε είναι ενεργή"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Μη διαθ. επειδή η λειτ. Μην ενοχλείτε είναι ενεργή"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Δεν διατίθεται επειδή η λειτουργία <xliff:g id="MODE">%s</xliff:g> είναι ενεργή"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Δεν διατίθεται"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Πατήστε για κατάργηση σίγασης."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Πατήστε για ενεργοποιήσετε τη δόνηση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Πατήστε για σίγαση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index ba945f229051..df9d2103b783 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"This device\'s music and videos will play on both pairs of headphones"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Share your audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> and <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Switch to <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Unavailable because <xliff:g id="MODE">%s</xliff:g> is on"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Unavailable"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index a26d1611f825..28cb9a311f5f 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"This device\'s music and videos will play on both pairs of headphones"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Share your audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> and <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Switch to <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Unavailable because <xliff:g id="MODE">%s</xliff:g> is on"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Unavailable"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index ba945f229051..df9d2103b783 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"This device\'s music and videos will play on both pairs of headphones"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Share your audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> and <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Switch to <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Unavailable because <xliff:g id="MODE">%s</xliff:g> is on"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Unavailable"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index ba945f229051..df9d2103b783 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"This device\'s music and videos will play on both pairs of headphones"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Share your audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> and <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Switch to <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Unavailable because <xliff:g id="MODE">%s</xliff:g> is on"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Unavailable"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 0620cc3fa068..e39b2a48f9ee 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartiendo audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ingresar la configuración de uso compartido de audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"La música y los videos de este dispositivo se reproducirán en ambos pares de auriculares"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Comparte tu audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> y <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Cambiar a <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Sí • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Sin establecer"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrar en configuración"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No hay modos activos}=1{{mode} está activo}many{# de modos están activos}other{# modos están activos}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas, los recordatorios, los eventos y las llamadas de los emisores que especifiques. Podrás escuchar el contenido que reproduzcas, como música, videos y juegos."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible por timbre silenciado"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"No disponible si está activado No interrumpir"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"No disponible si está activado No interrumpir"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"No disponible porque se activó <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"No disponible"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Presiona para dejar de silenciar."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Presiona para establecer el modo vibración. Es posible que los servicios de accesibilidad estén silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Presiona para silenciar. Es posible que los servicios de accesibilidad estén silenciados."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprende los gestos del panel táctil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega con el teclado y el panel táctil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende sobre los gestos del panel táctil, las combinaciones de teclas y mucho más"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Atrás"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a la página de inicio"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recientes"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Listo"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Desliza hacia la izquierda o la derecha con tres dedos en el panel táctil"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"¡Muy bien!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Completaste el gesto para ir atrás."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir a la página principal"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Desliza hacia arriba con tres dedos en el panel táctil"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"¡Bien hecho!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Completaste el gesto para ir a la página principal"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver apps recientes"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Desliza hacia arriba con tres dedos en el panel táctil y mantenlos presionados."</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"¡Bien hecho!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaste el gesto para ver las apps recientes."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las apps"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Presiona la tecla de acción en el teclado"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"¡Bien hecho!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Completaste el gesto para ver todas las apps"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index c10fc7ad17bf..6d77be4ff7cc 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartiendo audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"acceder a las opciones para compartir audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Los vídeos y la música de este dispositivo se reproducirán en ambos auriculares"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Comparte tu audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> y <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Cambiar a <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Sin definir"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestionar en los ajustes"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No hay modos activos}=1{{mode} está activo}many{Hay # modos activos}other{Hay # modos activos}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas, los recordatorios, los eventos y las llamadas que especifiques. Seguirás escuchando el contenido que quieras reproducir, como música, vídeos y juegos."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible (el tono está silenciado)"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"No disponible porque No molestar está activado"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"No disponible porque No molestar está activado"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"No disponible porque <xliff:g id="MODE">%s</xliff:g> está activado"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"No disponible"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar el sonido."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para poner el dispositivo en vibración. Los servicios de accesibilidad pueden silenciarse."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Los servicios de accesibilidad pueden silenciarse."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprende gestos del panel táctil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Desplázate con el teclado y el panel táctil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende gestos del panel táctil, combinaciones de teclas y más"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Volver"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a Inicio"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver aplicaciones recientes"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hecho"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Desliza hacia la izquierda o la derecha con tres dedos en el panel táctil"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"¡Genial!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Has completado el gesto para volver."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir a Inicio"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Desliza hacia arriba con tres dedos en el panel táctil"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"¡Bien hecho!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Has completado el gesto para ir a la pantalla de inicio"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver aplicaciones recientes"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Desliza hacia arriba y mantén pulsado con tres dedos en el panel táctil"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"¡Bien hecho!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completado el gesto para ver las aplicaciones recientes."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las aplicaciones"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pulsa la tecla de acción de tu teclado"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"¡Muy bien!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Has completado el gesto para ver todas las aplicaciones"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 4b97b7be2263..ed1440996b31 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Jaga heli"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Heli jagamine"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"heli jagamise seadete sisestamiseks"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Selle seadme muusikat ja videoid esitatakse mõlemas paaris kõrvaklappides"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Heli jagamine"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ja <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Vaheta seadmele <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Pole saadaval, kuna helin on vaigistatud"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Pole saadaval, kuna režiim Mitte segada on sees"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Pole saadaval, kuna režiim Mitte segada on sees"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Pole saadaval, kuna režiim <xliff:g id="MODE">%s</xliff:g> on sisse lülitatud"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Pole saadaval"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Puudutage vaigistuse tühistamiseks."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Puudutage värinarežiimi määramiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Puudutage vaigistamiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index eff98ce47e66..2fb75d31c156 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partekatu audioa"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioa partekatzen"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"audioa partekatzeko ezarpenetan sartzeko"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Gailu honetako musika eta bideoak 2 entzungailu pareetan erreproduzituko dira"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Partekatu zure audioa"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> eta <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Erabili <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ez dago erabilgarri, tonua desaktibatuta dagoelako"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ez dago erabilgarri, ez molestatzeko modua aktibatuta baitago"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ez dago erabilgarri, ez molestatzeko modua aktibatuta baitago"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ez dago erabilgarri, <xliff:g id="MODE">%s</xliff:g> aktibatuta dagoelako"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ez dago erabilgarri"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sakatu audioa aktibatzeko."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Sakatu dardara ezartzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sakatu audioa desaktibatzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 858670affe36..b18c84ad3507 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"همرسانی صدا"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"درحال همرسانی صدا"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"وارد شدن به تنظیمات «اشتراک صدا»"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"موسیقی و ویدیوهای این دستگاه در هر دو گوشی هدفون پخش خواهد شد"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"همرسانی کردن صدای دستگاه"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> و <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"رفتن به <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"دردسترس نیست، چون زنگ بیصدا شده است"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"دردسترس نیست زیرا «مزاحم نشوید» روشن است"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"دردسترس نیست زیرا «مزاحم نشوید» روشن است"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"دردسترس نیست زیرا <xliff:g id="MODE">%s</xliff:g> روشن است"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"دردسترس نیست"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. برای باصدا کردن تکضرب بزنید."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. برای تنظیم روی لرزش تکضرب بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. برای صامت کردن تکضرب بزنید. ممکن است سرویسهای دسترسپذیری صامت شود."</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 96a701af9f42..46027419ec7f 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Jaa audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audiota jaetaan"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"lisätäksesi audion jakamisasetukset"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Laitteen musiikki ja videot toistetaan molemmilla kuulokepareilla"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Jaa audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ja <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Vaihda: <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ei käytettävissä, soittoääni mykistetty"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ei saatavilla, koska Älä häiritse on päällä"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ei saatavilla, koska Älä häiritse on päällä"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ei käytettävissä, koska <xliff:g id="MODE">%s</xliff:g> on päällä"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ei saatavilla"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Poista mykistys koskettamalla."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Siirry värinätilaan koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Mykistä koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 6b718085398b..77434a5fd283 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partager l\'audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Partage de l\'audio en cours"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"entrer les paramètres de partage audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"La musique et les vidéos de cet appareil peuvent être lues sur les deux paires d\'écouteurs"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Partager votre audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> et <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Passer à <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Activé"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activé • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Désactivé"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Non défini"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gérer dans les paramètres"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Aucun mode actif}=1{Le mode {mode} est actif}one{# mode est actif}many{# de modes sont actifs}other{# modes sont actifs}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé par les sons et les vibrations, sauf pour les alarmes, les rappels, les événements et les appelants que vous sélectionnez. Vous entendrez tout ce que vous choisissez d\'écouter, y compris la musique, les vidéos et les jeux."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Inaccessible : sonnerie en sourdine"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Inaccessible parce que Ne pas déranger est activée"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Inaccessible parce que Ne pas déranger est activée"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Non accessible, car le mode <xliff:g id="MODE">%s</xliff:g> est activé"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Non accessible"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Touchez pour réactiver le son."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Touchez pour activer les vibrations. Il est possible de couper le son des services d\'accessibilité."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Touchez pour couper le son. Il est possible de couper le son des services d\'accessibilité."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Apprenez à utiliser les gestes du pavé tactile"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviguer à l\'aide de votre clavier et de votre pavé tactile"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Apprenez les gestes du pavé tactile, les raccourcis-clavier et bien plus encore"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Retour"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Retour à la page d\'accueil"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Balayez votre pavé tactile vers la gauche ou vers la droite avec trois doigts"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bien!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Vous avez appris le geste de retour en arrière."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Retour à la page d\'accueil"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Balayez votre pavé tactile vers le haut avec trois doigts"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bon travail!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Vous avez appris le geste pour revenir à l\'écran d\'accueil"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Afficher les applis récentes"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Balayez votre pavé tactile vers le haut avec trois doigts, puis maintenez-les en place"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bon travail!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Vous avez effectué le geste pour afficher les applis récentes."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Afficher toutes les applis"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Félicitations!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Vous avez appris le geste pour afficher toutes les applis"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index dccb581d88bf..fa16862ac1d7 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partager le contenu audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio partagé"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"accéder aux paramètres de partage audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"La musique et les vidéos de cet appareil peuvent être lues sur les deux casques"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Partager votre audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> et <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Passer à <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Activé"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activé • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Désactivé"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Non défini"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gérer dans les paramètres"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Aucun mode actif}=1{{mode} est actif}one{# mode est actif}many{# de modes sont actifs}other{# modes sont actifs}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes, des rappels, des événements et des appelants de votre choix. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponible, car la sonnerie est coupée"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponible car Ne pas déranger est activé"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponible car Ne pas déranger est activé"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Indisponible, car le mode <xliff:g id="MODE">%s</xliff:g> est activé"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Indisponible"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Appuyez pour ne plus ignorer."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Appuyez pour mettre en mode vibreur. Vous pouvez ignorer les services d\'accessibilité."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Appuyez pour ignorer. Vous pouvez ignorer les services d\'accessibilité."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Découvrir les gestes au pavé tactile"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviguer à l\'aide de votre clavier et de votre pavé tactile"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Découvrir les gestes au pavé tactile, les raccourcis clavier et plus encore"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Retour"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Retour à l\'accueil"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Balayez vers la gauche ou la droite avec trois doigts sur le pavé tactile"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bravo !"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Vous avez appris le geste pour revenir en arrière."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Retour à l\'accueil"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Balayez vers le haut avec trois doigts sur le pavé tactile"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bravo !"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Vous avez appris le geste pour revenir à l\'écran d\'accueil"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Afficher les applis récentes"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Avec trois doigts, balayez le pavé tactile vers le haut et maintenez la position"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bravo !"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Vous avez appris le geste pour afficher les applis récentes"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Afficher toutes les applications"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bravo !"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Vous avez appris le geste pour afficher toutes les applis"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index b0a432d9ee93..1d2c785a198e 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartindo audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"configurar o uso compartido de audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"A música e os vídeos deste dispositivo reproduciranse nos dous pares de auriculares"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Compartir o audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> e <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Cambiar a <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Non dispoñible: o son está silenciado"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Non dispoñible: Non molestar está activado"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Non dispoñible: Non molestar está activado"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Non está dispoñible porque se activou o modo <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Non dispoñible"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar o son."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para establecer a vibración. Pódense silenciar os servizos de accesibilidade."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Pódense silenciar os servizos de accesibilidade."</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 5de229333397..c6014b18e4f6 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ઑડિયો શેર કરો"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ઑડિયો શેર કરી રહ્યાં છીએ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ઑડિયો શેરિંગ સેટિંગ દાખલ કરો"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"આ ડિવાઇસનું મ્યુઝિક અને વીડિયો હૅડફોનની બન્ને જોડીઓ પર ચાલશે"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"તમારો ઑડિયો શેર કરો"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> અને <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> પર સ્વિચ કરો"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"રિંગ મ્યૂટ કરી હોવાના કારણે અનુપલબ્ધ છે"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ઉપલબ્ધ નથી, કારણ કે ખલેલ પાડશો નહીં મોડ ચાલુ છે"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ઉપલબ્ધ નથી, કારણ કે ખલેલ પાડશો નહીં મોડ ચાલુ છે"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ચાલુ હોવાથી અનુપલબ્ધ છે"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"અનુપલબ્ધ છે"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. અનમ્યૂટ કરવા માટે ટૅપ કરો."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. વાઇબ્રેટ પર સેટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 15b363962859..28a4f2bc05eb 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ऑडियो शेयर करें"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ऑडियो शेयर किया जा रहा है"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ऑडियो शेयर करने की सेटिंग जोड़ें"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"इस डिवाइस के संगीत और वीडियो, दोनों हेडफ़ोन पर चलेंगे"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"अपना ऑडियो शेयर करें"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> और <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> पर स्विच करें"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"आवाज़ नहीं आएगी, क्योंकि रिंग म्यूट है"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"सुविधा बंद है, क्योंकि \'परेशान न करें\' मोड चालू है"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"आवाज़ बंद है, क्योंकि \'परेशान न करें\' मोड चालू है"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> चालू होने की वजह से यह सुविधा उपलब्ध नहीं है"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"उपलब्ध नहीं है"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करने के लिए टैप करें."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. कंपन पर सेट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index f646ee613611..cc19bea1fee0 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Dijeli zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zajedničko slušanje"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"unesite postavke zajedničkog slušanja"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Glazba i videozapisi s ovog uređaja reproducirat će se na oba para slušalica"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Podijelite zvuk"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> i <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Prijeđi na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvono utišano"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je uključen način Ne uznemiravaj"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupno jer je uključen način Ne uznemiravaj"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nedostupno jer je uključen način <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupno"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da biste uključili zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite da biste postavili na vibraciju. Usluge pristupačnosti možda neće imati zvuk."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Usluge pristupačnosti možda neće imati zvuk."</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 1248f41aa8ae..9ee748bdc04c 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Hang megosztása"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Hang megosztása…"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"a hangmegosztási beállítások megadásához"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Az eszközön lejátszott zene és videó a fejhallgatópárt alkotó mindkét eszközön hallható"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Hang megosztása"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> és <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Váltás erre: <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nem lehetséges, a csörgés le van némítva"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nem működik, mert be van kapcsolva a Ne zavarjanak"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nem működik, mert be van kapcsolva a Ne zavarjanak"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nem áll rendelkezésre, mert a(z) <xliff:g id="MODE">%s</xliff:g> be van kapcsolva"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nem áll rendelkezésre"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Koppintson a némítás megszüntetéséhez."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Koppintson a rezgés beállításához. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Koppintson a némításhoz. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index db58b355f6d2..b07a78522437 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Փոխանցել աուդիոն"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Աուդիոյի փոխանցում"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"անցնել աուդիոյի փոխանցման կարգավորումներ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Այս սարքի երաժշտությունն ու տեսանյութերը երկու ականջակալներում էլ կնվագարկվեն"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Կիսվեք ձեր աուդիոյով"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> և <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Անցնել <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> սարքին"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Միացված է"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Միաց․ • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Անջատված է"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Կարգավորված չէ"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Կառավարել կարգավորումներում"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ակտիվ ռեժիմներ չկան}=1{{mode} ռեժիմ ակտիվ է}one{# ռեժիմ ակտիվ է}other{# ռեժիմ ակտիվ է}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Ձայները և թրթռոցները չեն անհանգստացնի ձեզ, բացի ձեր կողմից նշված զարթուցիչները, հիշեցումները, միջոցառումների ծանուցումները և զանգերը։ Դուք կլսեք ձեր ընտրածի նվագարկումը, այդ թվում՝ երաժշտություն, տեսանյութեր և խաղեր:"</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Հասանելի չէ, երբ զանգի ձայնն անջատված է"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Հասանելի չէ, քանի որ «<xliff:g id="MODE">%s</xliff:g>» ռեժիմը միացված է"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Հասանելի չէ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s: Հպեք՝ ձայնը միացնելու համար:"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s: Հպեք՝ թրթռոցը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s: Հպեք՝ ձայնն անջատելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Սովորեք օգտագործել հպահարթակի ժեստերը"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Կողմնորոշվեք ստեղնաշարի և հպահարթակի օգնությամբ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Սովորեք օգտագործել հպահարթակի ժեստերը, ստեղնային դյուրանցումները և ավելին"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Ինչպես հետ գնալ"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ինչպես անցնել հիմնական էկրան"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Դիտել վերջին հավելվածները"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Պատրաստ է"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Հետ գնալ"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Հպահարթակի վրա երեք մատով սահեցրեք ձախ կամ աջ"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Գերազանց է"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Դուք սովորեցիք հետ գնալու ժեստը։"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Անցում հիմնական էկրան"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Հպահարթակի վրա երեք մատով սահեցրեք վերև"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Կեցցե՛ք"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Դուք սովորեցիք հիմնական էկրան անցնելու ժեստը"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Դիտել վերջին հավելվածները"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Երեք մատով սահեցրեք վերև և սեղմած պահեք հպահարթակին"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Կեցցե՛ք։"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Դուք կատարեցիք վերջին օգտագործված հավելվածների դիտման ժեստը։"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ինչպես դիտել բոլոր հավելվածները"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Հիանալի՛ է"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Դուք սովորեցիք բոլոր հավելվածները դիտելու ժեստը"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 2c2927553ea0..84a45c6c8a08 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Bagikan audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Berbagi audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"masuk ke setelan berbagi audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Musik dan video di perangkat ini akan diputar di kedua pasang headphone"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Bagikan audio Anda"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> dan <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Alihkan ke <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia - Volume dering dibisukan"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Tidak tersedia - Fitur Jangan Ganggu aktif"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Tidak tersedia - Fitur Jangan Ganggu aktif"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Tidak tersedia karena <xliff:g id="MODE">%s</xliff:g> aktif"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Tidak tersedia"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ketuk untuk menyuarakan."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ketuk untuk menyetel agar bergetar. Layanan aksesibilitas mungkin dibisukan."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketuk untuk membisukan. Layanan aksesibilitas mungkin dibisukan."</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 1897ba414d57..f27790c05b1b 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deila hljóði"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deilir hljóði"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"slá inn stillingar hljóðdeilingar"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Tónlist og vídeó í tækinu munu spilast í báðum heyrnartólum"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Deildu hljóðinu þínu"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> og <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Skipta yfir í <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ekki í boði þar sem hringing er þögguð"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ekki í boði því að kveikt er á „Ónáðið ekki“"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ekki í boði því að kveikt er á „Ónáðið ekki“"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ekki í boði vegna þess að kveikt er á <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ekki í boði"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ýttu til að hætta að þagga."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ýttu til að stilla á titring. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ýttu til að þagga. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 2931b96f0888..7ace302022b6 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Condividi audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Condivisione audio in corso…"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"inserisci le impostazioni di condivisione audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"La musica e i video di questo dispositivo verranno riprodotti su entrambe le coppie di cuffie"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Condividi l\'audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> e <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Passa a <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"On"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Non impostata"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestisci nelle impostazioni"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nessuna modalità attiva}=1{{mode} è attiva}many{# di modalità sono attive}other{# modalità sono attive}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Suoni e vibrazioni non ti disturberanno, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Non disponibile con l\'audio disattivato"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Non disponibile: modalità Non disturbare attiva"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Non disponibili con \"Non disturbare\" attiva"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Non disponibile perché la modalità <xliff:g id="MODE">%s</xliff:g> è attiva"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Non disponibile"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tocca per riattivare l\'audio."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tocca per attivare la vibrazione. L\'audio dei servizi di accessibilità può essere disattivato."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tocca per disattivare l\'audio. L\'audio dei servizi di accessibilità può essere disattivato."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Impara i gesti con il touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviga usando la tastiera e il touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Scopri gesti con il touchpad, scorciatoie da tastiera e altro ancora"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Indietro"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Vai alla schermata Home"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Visualizza app recenti"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fine"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Indietro"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Scorri verso sinistra o destra con tre dita sul touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bene!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Hai completato il gesto Indietro."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Vai alla schermata Home"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Scorri in alto con tre dita sul touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Ottimo lavoro."</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Hai completato il gesto Vai alla schermata Home"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Visualizza app recenti"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Scorri verso l\'alto e tieni premuto con tre dita sul touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Ottimo lavoro."</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Hai completato il gesto Visualizza app recenti."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Visualizza tutte le app"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Premi il tasto azione sulla tastiera"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Ben fatto!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Hai completato il gesto Visualizza tutte le app."</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 72fc195b056f..b5783cf56688 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"שיתוף האודיו"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"מתבצע שיתוף של האודיו"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"להזנת הרשאות השיתוף של האודיו"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"המוזיקה והסרטונים של המכשיר הזה יופעלו בשני זוגות האוזניות"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"שיתוף האודיו שלך"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> וגם <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"מעבר אל <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"לא זמין כי הצלצול מושתק"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"לא זמין כי התכונה \'נא לא להפריע\' מופעלת"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"לא זמין כי התכונה \'נא לא להפריע\' מופעלת"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"לא זמין כי המצב <xliff:g id="MODE">%s</xliff:g> מופעל"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"לא זמין"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. יש להקיש כדי לבטל את ההשתקה."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. צריך להקיש כדי להגדיר רטט. ייתכן ששירותי הנגישות מושתקים."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. יש להקיש כדי להשתיק. ייתכן ששירותי הנגישות יושתקו."</string> @@ -1425,7 +1427,7 @@ <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות על לוח המגע"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"מעולה!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"השלמת את התנועה להצגת האפליקציות האחרונות."</string> - <string name="tutorial_action_key_title" msgid="8172535792469008169">"הצגת כל האפליקציות"</string> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"צפייה בכל האפליקציות"</string> <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"צריך להקיש על מקש הפעולה במקלדת"</string> <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"כל הכבוד!"</string> <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"השלמת את התנועה להצגת כל האפליקציות"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index dcbfc8ce69be..8427f6709d31 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"音声を共有"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"音声を共有中"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"音声の共有設定を開く"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"このデバイスの音楽と動画は、両方のヘッドフォンで再生されます"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"音声の共有"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>、<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> に切り替える"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"着信音がミュートされているため利用できません"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"サイレント モードが ON のため利用できません"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"サイレント モードが ON のため利用できません"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g>が ON のため使用できません"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"使用不可"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。タップしてミュートを解除します。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。タップしてバイブレーションに設定します。ユーザー補助機能サービスがミュートされる場合があります。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。タップしてミュートします。ユーザー補助機能サービスがミュートされる場合があります。"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 65d85756c4d9..3b0732da57c4 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"აუდიოს გაზიარება"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"მიმდინარებოს აუდიოს გაზიარება"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"აუდიო გაზიარების პარამეტრების შეყვანა"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ამ მოწყობილობის მუსიკა და ვიდეოები დაუკრავს ორივე ყურსასმენში"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"თქვენი აუდიოს გაზიარება"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> და <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>-ზე გადართვა"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ზარის დადუმების გამო ხელმისაწვდომი არაა"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"მიუწვდომელია, ჩართულია „არ შემაწუხოთ“ რეჟიმი"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"მიუწვდომელია, ჩართულია „არ შემაწუხოთ“ რეჟიმი"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> რეჟიმის გამო მიუწვდომელია"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"მიუწვდომელია"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. შეეხეთ დადუმების გასაუქმებლად."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. შეეხეთ ვიბრაციაზე დასაყენებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. შეეხეთ დასადუმებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 7478e7631bb8..65115583f681 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Аудионы бөлісу"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Аудио беріліп жатыр"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"аудио бөлісу параметрлерін енгізу"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Бұл құрылғыдағы музыка мен бейнелер қос құлақаспапта ойнатылады."</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Аудиоңызды бөлісіңіз"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> және <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> құрылғысына ауысу"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Қолжетімді емес, шылдырлату өшірулі."</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Мазаламау режимі қосылғандықтан өзгерту мүмкін емес."</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Мазаламау режимі қосылғандықтан өзгерту мүмкін емес."</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Қолжетімді емес, себебі <xliff:g id="MODE">%s</xliff:g> режимі қосулы."</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Қолжетімді емес"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дыбысын қосу үшін түртіңіз."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Діріл режимін орнату үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дыбысын өшіру үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index d0f3f848fca9..7b7f912b3e1c 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ស្ដាប់សំឡេងរួមគ្នា"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"កំពុងស្ដាប់សំឡេងរួមគ្នា"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"បញ្ចូលការកំណត់ការស្ដាប់សំឡេងរួមគ្នា"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"តន្ត្រី និងវីដេអូរបស់ឧបករណ៍នេះនឹងចាក់នៅលើកាសទាំងពីរគូ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ស្ដាប់សំឡេងរបស់អ្នករួមគ្នា"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> និង <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"ប្ដូរទៅ <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"មិនអាចប្រើបានទេ ព្រោះសំឡេងរោទ៍ត្រូវបានបិទ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"មិនអាចប្រើបានទេ ដោយសារមុខងារកុំរំខានត្រូវបានបើក"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"មិនអាចប្រើបានទេ ដោយសារមុខងារកុំរំខានត្រូវបានបើក"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"មិនអាចប្រើបានទេ ដោយសារបើក <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"មិនមានទេ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s។ ប៉ះដើម្បីបើកសំឡេង។"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s។ ប៉ះដើម្បីកំណត់ឲ្យញ័រ។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s។ ប៉ះដើម្បីបិទសំឡេង។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 57a8cdf2fa66..c4417b7990b0 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳಿ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ನಮೂದಿಸಿ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ಈ ಸಾಧನದ ಸಂಗೀತ ಮತ್ತು ವೀಡಿಯೊಗಳು ಎರಡೂ ಜೋಡಿ ಹೆಡ್ಫೋನ್ಗಳಲ್ಲಿ ಪ್ಲೇ ಆಗುತ್ತವೆ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ನಿಮ್ಮ ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ಮತ್ತು <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್ಸೆಟ್"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ರಿಂಗ್ ಮ್ಯೂಟ್ ಆಗಿರುವ ಕಾರಣ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಫೀಚರ್ ಆನ್ ಆಗಿರುವುದರಿಂದ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಫೀಚರ್ ಆನ್ ಆಗಿರುವುದರಿಂದ ಲಭ್ಯವಿಲ್ಲ"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ಆನ್ ಆಗಿರುವ ಕಾರಣ ಲಭ್ಯವಿಲ್ಲ"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ಲಭ್ಯವಿಲ್ಲ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ಅನ್ಮ್ಯೂಟ್ ಮಾಡುವುದಕ್ಕಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. ಕಂಪನಕ್ಕೆ ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 9775ad5d9bf1..5602ee79eb37 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"오디오 공유"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"오디오 공유 중"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"오디오 공유 설정으로 이동"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"이 기기의 음악 및 동영상이 헤드폰 양쪽에서 재생됩니다."</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"오디오 공유"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> 및 <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"기기(<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>)로 전환"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"벨소리가 음소거되어 있으므로 사용할 수 없음"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"방해 금지 모드로 설정되어 있어 사용할 수 없음"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"방해 금지 모드로 설정되어 있어 사용할 수 없음"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> 모드가 사용 설정되어 있어 사용할 수 없음"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"사용할 수 없음"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. 탭하여 음소거를 해제하세요."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. 탭하여 진동으로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. 탭하여 음소거로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 80b75b933cca..93777db15280 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Чогуу угуу"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Чогуу угулууда"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"чогуу угуу параметрлерин киргизүү"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Бул түзмөктөгү музыка жана видеолор гарнитуранын эки кулагынан тең угулат"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Чогуу угуңуз"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> жана <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> түзмөгүнө которулуу"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Үнсүз режимде жеткиликсиз"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\"Тынчымды алба\" режими күйүк болгондуктан, жеткиликсиз"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"\"Тынчымды алба\" режими күйүк болгондуктан, жеткиликсиз"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> күйүп тургандыктан жеткиликсиз"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Жеткиликсиз"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Үнүн чыгаруу үчүн таптап коюңуз."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Дирилдөөгө коюу үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Үнүн өчүрүү үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string> diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml index b5efeb5f6b3b..5d5b95546d0d 100644 --- a/packages/SystemUI/res/values-land/config.xml +++ b/packages/SystemUI/res/values-land/config.xml @@ -25,6 +25,12 @@ <integer name="quick_settings_num_columns">4</integer> + <!-- The number of rows in the paginated grid QuickSettings --> + <integer name="quick_settings_paginated_grid_num_rows">2</integer> + + <!-- The number of rows in the paginated grid QuickQuickSettings --> + <integer name="quick_qs_paginated_grid_num_rows">1</integer> + <!-- The number of columns in the infinite grid QuickSettings --> <integer name="quick_settings_infinite_grid_num_columns">8</integer> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 3a09b2a25995..07f3853b3c8b 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ແບ່ງປັນສຽງ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ກຳລັງແບ່ງປັນສຽງ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ເຂົ້າສູ່ການຕັ້ງຄ່າການແບ່ງປັນສຽງ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ເພງ ແລະ ວິດີໂອຂອງອຸປະກອນເຄື່ອງນີ້ຈະຫຼິ້ນຢູ່ຫູຟັງທັງສອງຄູ່"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ແບ່ງປັນສຽງຂອງທ່ານ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ແລະ <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"ປ່ຽນເປັນ <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ບໍ່ມີໃຫ້ໃຊ້ເນື່ອງຈາກມີການປິດສຽງໂທເຂົ້າ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ບໍ່ມີໃຫ້ຍ້ອນວ່າຫ້າມລົບກວນເປີດຢູ່"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ບໍ່ມີໃຫ້ຍ້ອນວ່າຫ້າມລົບກວນເປີດຢູ່"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"ບໍ່ສາມາດໃຊ້ໄດ້ເນື່ອງຈາກ <xliff:g id="MODE">%s</xliff:g> ເປີດຢູ່"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ບໍ່ສາມາດໃຊ້ໄດ້"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ແຕະເພື່ອເຊົາປິດສຽງ."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. ແຕະເພື່ອຕັ້ງເປັນສັ່ນ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ແຕະເພື່ອປິດສຽງ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index f3f383e4d418..605b8dd2e303 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Bendrinti garsą"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Bendrinamas garsas"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"įvesti garso įrašų bendrinimo nustatymus"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Šio įrenginio muzika ir vaizdo įrašai bus leidžiami abiejose ausinėse"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Garso įrašo bendrinimas"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ir <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Perjungti į <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nepasiekiama, nes skambutis nutildytas"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nepasiekiama, nes įjungtas netrukdymo režimas"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nepasiekiama, nes įjungtas netrukdymo režimas"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nepasiekiama, nes įjungtas režimas „<xliff:g id="MODE">%s</xliff:g>“"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nepasiekiama"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Palieskite, kad įjungtumėte garsą."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Palieskite, kad nustatytumėte vibravimą. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Palieskite, kad nutildytumėte. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 9c22eed6bbd9..0393ad50a619 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Kopīgot audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Notiek audio kopīgošana…"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"atvērt audio kopīgošanas iestatījumus"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Mūzika un videoklipi no šīs ierīces tiks atskaņoti abās austiņās"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Audio kopīgošana"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> un <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Pārslēgties uz šādu ierīci: <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nevar mainīt, jo izslēgts skaņas signāls"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nav pieejams, jo ir ieslēgts režīms Netraucēt"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nav pieejams, jo ir ieslēgts režīms Netraucēt"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nav pieejams, jo ir ieslēgts režīms <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nav pieejams"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Pieskarieties, lai ieslēgtu skaņu."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Pieskarieties, lai iestatītu uz vibrozvanu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Pieskarieties, lai izslēgtu skaņu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 6e37907b6ffb..6b956ec6f532 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Споделувај аудио"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Се споделува аудио"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"внесете ги поставките за „Споделување аудио“"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музиката и видеата на уредов ќе се пуштаaт на двата пара слушалки"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Споделете го аудиото"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> и <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Префрли на <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Вклучено"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Вклучено: <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Исклучено"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Не е поставено"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Управувајте во поставките"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Нема активни режими}=1{Активен е {mode}}one{Активни се # режим}other{Активни се # режими}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Нема да ве вознемируваат звуци и вибрации, освен од аларми, потсетници, настани и повикувачи што ќе ги наведете. Сѐ уште ќе слушате сѐ што ќе изберете да пуштите, како музика, видеа и игри."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недостапно бидејќи звукот е исклучен"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недостапно бидејќи е вклучено „Не вознемирувај“"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Недостапно бидејќи е вклучено „Не вознемирувај“"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недостапно бидејќи има вклучено <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недостапно"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Допрете за да вклучите звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Допрете за да поставите на вибрации. Можеби ќе се исклучи звукот на услугите за достапност."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Допрете за да исклучите звук. Можеби ќе се исклучи звукот на услугите за достапност."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Научете движења за допирната подлога"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Движете се со користење на тастатурата и допирната подлога"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научете движења за допирната подлога, кратенки од тастатурата и друго"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Врати се назад"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Оди на почетниот екран"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прегледајте ги неодамнешните апликации"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Повлечете налево или надесно со три прста на допирната подлога"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Одлично!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Го научивте движењето за враќање назад."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Одете на почетниот екран"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Повлечете нагоре со три прсти на допирната подлога"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Одлично!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Го завршивте движењето за враќање на почетниот екран"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прегледајте ги неодамнешните апликации"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Повлечете нагоре и задржете со три прста на допирната подлога"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Го завршивте движењето за прегледување на неодамнешните апликации."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Прегледајте ги сите апликации"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притиснете го копчето за дејство на тастатурата"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Браво!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Го завршивте движењето за прегледување на сите апликации"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за домот"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index bf446d988eb7..cd0a0442d3b0 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ഓഡിയോ പങ്കിടുക"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ഓഡിയോ പങ്കിടുന്നു"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ഓഡിയോ പങ്കിടൽ ക്രമീകരണം നൽകുക"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"രണ്ട് ജോടി ഹെഡ്ഫോണുകളിലൂടെയും ഈ ഉപകരണത്തിലെ സംഗീതവും വീഡിയോകളും പ്ലേ ചെയ്യും"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"നിങ്ങളുടെ ഓഡിയോ പങ്കിടുക"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>, <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> എന്നതിലേക്ക് മാറുക"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്സെറ്റ്"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"റിംഗ് മ്യൂട്ട് ചെയ്തതിനാൽ ലഭ്യമല്ല"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ശല്യപ്പെടുത്തരുത് ഓണായതിനാൽ ലഭ്യമല്ല"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ശല്യപ്പെടുത്തരുത് ഓണായതിനാൽ ലഭ്യമല്ല"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ഓണായതിനാൽ ലഭ്യമല്ല"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ലഭ്യമല്ല"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. അൺമ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. വൈബ്രേറ്റിലേക്ക് സജ്ജമാക്കുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. മ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 1bd6768e93de..e4f174641043 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Аудио хуваалцах"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Аудио хуваалцаж байна"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"аудио хуваалцах тохиргоог оруулах"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Энэ төхөөрөмжийн хөгжим, видеонуудыг чихэвчийн хоёр талд тоглуулна"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Аудиогоо хуваалцах"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>, <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> руу сэлгэх"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Асаалттай"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Асаасан • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Унтраалттай"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Тохируулаагүй"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Тохиргоонд удирдах"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ямар ч идэвхтэй горим байхгүй}=1{{mode} идэвхтэй байна}other{# горим идэвхтэй байна}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Танд сэрүүлэг, сануулга, арга хэмжээ, таны сонгосон дуудлага илгээгчээс бусад дуу, чичиргээ саад болохгүй. Та хөгжим, видео, тоглоом зэрэг тоглуулахыг хүссэн бүх зүйлээ сонсох боломжтой хэвээр байна."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Хонхны дууг хаасан тул боломжгүй"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Бүү саад бол асаалттай тул боломжгүй"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Бүү саад бол асаалттай тул боломжгүй"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> асаалттай тул боломжгүй"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Боломжгүй"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дууг нь нээхийн тулд товшино уу."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Чичиргээнд тохируулахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дууг нь хаахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Мэдрэгч самбарын зангааг мэдэж аваарай"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Гар эсвэл мэдрэгч самбараа ашиглан шилжээрэй"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Мэдрэгч самбарын зангаа, товчлуурын шууд холбоос болон бусад зүйлийг мэдэж аваарай"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Буцах"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Нүүр хуудас руу очих"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Саяхны аппуудыг харах"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Болсон"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Буцах"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Мэдрэгч самбар дээрээ гурван хуруугаа ашиглан зүүн эсвэл баруун тийш шударна уу"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Янзтай!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Та буцах зангааг гүйцэтгэлээ."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Үндсэн нүүр лүү очих"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Мэдрэгч самбар дээрээ гурван хуруугаараа дээш шударна уу"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Үнэхээр сайн ажиллалаа!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Та үндсэн нүүр лүү очих зангааг гүйцэтгэлээ"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Саяхны аппуудыг харах"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Мэдрэгч самбар дээрээ гурван хуруугаа ашиглан дээш шудраад, удаан дарна уу"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Сайн байна!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Та саяхны аппуудыг харах зангааг гүйцэтгэсэн."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Бүх аппыг харах"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээр тань байх тусгай товчийг дарна уу"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Сайн байна!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Та бүх аппыг харах зангааг гүйцэтгэлээ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-с %1$d-р түвшин"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Гэрийн удирдлага"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 60718b9e4172..458fe2cb3c84 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ऑडिओ शेअर करा"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ऑडिओ शेअर करत आहे"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ऑडिओ शेअरिंग सेटिंग्ज एंटर करा"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"या डिव्हाइसचे संगीत आणि व्हिडिओ हेडफोनच्या दोन्ही पेअरवर प्ले होतील"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"तुमचा ऑडिओ शेअर करा"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> आणि <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> वर स्विच करा"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"रिंग म्यूट केल्यामुळे उपलब्ध नाही"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"व्यत्यय आणू नका हे सुरू असल्यामुळे उपलब्ध नाही"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"व्यत्यय आणू नका हे सुरू असल्यामुळे उपलब्ध नाही"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> सुरू असल्यामुळे उपलब्ध नाही"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"उपलब्ध नाही"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करण्यासाठी टॅप करा."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा म्यूट केल्या जाऊ शकतात."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा म्यूट केल्या जाऊ शकतात."</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 1160a1af486e..8912366fbed4 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Kongsi audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Perkongsian audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"masukkan tetapan perkongsian audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzik dan video peranti ini akan dimainkan pada kedua-dua pasang fon kepala"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Kongsi audio anda"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> dan <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Beralih kepada <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia kerana deringan diredam"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Tidak tersedia kerana Jangan Ganggu dihidupkan"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Tidak tersedia kerana Jangan Ganggu dihidupkan"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Tidak tersedia kerana <xliff:g id="MODE">%s</xliff:g> dihidupkan"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Tidak tersedia"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ketik untuk menyahredam."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ketik untuk menetapkan pada getar. Perkhidmatan kebolehaksesan mungkin diredamkan."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketik untuk meredam. Perkhidmatan kebolehaksesan mungkin diredamkan."</string> @@ -1414,7 +1416,7 @@ <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Lihat apl terbaharu"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string> - <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Leret ke kiri atau kanan menggunakan tiga jari pada pad sentuh anda"</string> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Leret ke kiri atau ke kanan menggunakan tiga jari pada pad sentuh anda"</string> <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bagus!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Anda telah melengkapkan gerak isyarat undur."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Akses laman utama"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 224247eab689..e3880017fde4 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"အသံမျှဝေရန်"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"အသံမျှဝေနေသည်"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"အော်ဒီယို မျှဝေခြင်း ဆက်တင်များ ထည့်ရန်"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ဤစက်၏ သီချင်းနှင့် ဗီဒီယိုများကို နားကြပ်နှစ်ဖက်စလုံးတွင် ဖွင့်မည်"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"သင့်အသံ မျှဝေရန်"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> နှင့် <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> သို့ ပြောင်းရန်"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ဖုန်းမြည်သံပိတ်ထားသဖြင့် မရနိုင်ပါ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"‘မနှောင့်ယှက်ရ’ ဖွင့်ထားသောကြောင့် မရနိုင်ပါ"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"‘မနှောင့်ယှက်ရ’ ဖွင့်ထားသောကြောင့် မရနိုင်ပါ"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ကို ဖွင့်ထားသဖြင့် မရနိုင်ပါ"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"မရနိုင်ပါ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s။ အသံပြန်ဖွင့်ရန် တို့ပါ။"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s။ တုန်ခါမှုကို သတ်မှတ်ရန် တို့ပါ။ အများသုံးနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s။ အသံပိတ်ရန် တို့ပါ။ အများသုံးနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 6d65c6c1640d..49c004b362e7 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Del lyd"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deler lyd"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"åpne innstillingene for lyddeling"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Musikk og videoer fra denne enheten spilles av via begge hodetelefonparene"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Del lyd"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> og <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Bytt til <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Utilgjengelig fordi ringelyden er kuttet"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Utilgjengelig fordi «Ikke forstyrr» er på"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Utilgjengelig fordi «Ikke forstyrr» er på"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Utilgjengelig fordi <xliff:g id="MODE">%s</xliff:g> er på"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Utilgjengelig"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Trykk for å slå på lyden."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Trykk for å angi vibrasjon. Lyden kan bli slått av for tilgjengelighetstjenestene."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trykk for å slå av lyden. Lyden kan bli slått av for tilgjengelighetstjenestene."</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index a55c53a74042..e364e9ec3edf 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"अडियो सेयर गर्नुहोस्"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"अडियो सेयर गरिँदै छ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"अडियो सेयर गर्ने सुविधासम्बन्धी सेटिङ हाल्न"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"यो डिभाइसका सङ्गीत र भिडियोहरू दुवै जोडी हेडफोनमा प्ले हुने छन्"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"आफ्नो अडियो सेयर गर्नुहोस्"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> र <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> चलाउनुहोस्"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"डिभाइस म्युट गरिएकाले यो सुविधा उपलब्ध छैन"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Do Not Disturb अन भएकाले भोल्युम बढाउन मिल्दैन"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Do Not Disturb अन भएकाले भोल्युम बढाउन मिल्दैन"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> अन भएकाले यो सुविधा उपलब्ध छैन"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"उपलब्ध छैन"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। अनम्यूट गर्नाका लागि ट्याप गर्नुहोस्।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। कम्पनमा सेट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। म्यूट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index e5bc5be40235..0f8eb1146251 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audio delen"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio delen"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"instellingen voor audio delen openen"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"De muziek en video\'s van dit apparaat worden op beide koptelefoons afgespeeld"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Je audio delen"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> en <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Overschakelen naar <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niet beschikbaar, belgeluid staat uit"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Niet beschikbaar omdat Niet storen aanstaat"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Niet beschikbaar omdat Niet storen aanstaat"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Niet beschikbaar omdat <xliff:g id="MODE">%s</xliff:g> aanstaat"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Niet beschikbaar"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om dempen op te heffen."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om in te stellen op trillen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te dempen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index cf7a58d763c9..16127eb93ade 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ଅଡିଓ ସେୟାର କରନ୍ତୁ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ଅଡିଓ ସେୟାର କରାଯାଉଛି"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ଅଡିଓ ସେୟାରିଂ ସେଟିଂସରେ ପ୍ରବେଶ କରନ୍ତୁ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ଏହି ଡିଭାଇସର ମ୍ୟୁଜିକ ଏବଂ ଭିଡିଓଗୁଡ଼ିକ ଉଭୟ ପେୟାର ହେଡଫୋନରେ ପ୍ଲେ ହେବ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ଆପଣଙ୍କ ଅଡିଓ ସେୟାର କରନ୍ତୁ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ଏବଂ <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>କୁ ସୁଇଚ କରନ୍ତୁ"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ରିଂକୁ ମ୍ୟୁଟ କରାଯାଇଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\' ଚାଲୁ ଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"\'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\' ଚାଲୁ ଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ଚାଲୁ ଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। ଅନମ୍ୟୁଟ୍ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। ଭାଇବ୍ରେଟ୍ ସେଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍ ମ୍ୟୁଟ୍ କରାଯାଇପାରେ।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ମ୍ୟୁଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍ ମ୍ୟୁଟ୍ କରାଯାଇପାରେ।"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 43691a4bef1b..63de1a6bda61 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕਰੋ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ ਸੈਟਿੰਗਾਂ ਦਾਖਲ ਕਰੋ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ਇਸ ਡੀਵਾਈਸ ਦਾ ਸੰਗੀਤ ਅਤੇ ਵੀਡੀਓ ਹੈੱਡਫ਼ੋਨਾਂ ਦੇ ਦੋਵਾਂ ਜੋੜਿਆਂ \'ਤੇ ਚੱਲਣਗੇ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ਆਪਣਾ ਆਡੀਓ ਸਾਂਝਾ ਕਰੋ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ਅਤੇ <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਘੰਟੀ ਮਿਊਟ ਕੀਤੀ ਹੋਈ ਹੈ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਵਿਸ਼ੇਸ਼ਤਾ ਚਾਲੂ ਹੈ"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਵਿਸ਼ੇਸ਼ਤਾ ਚਾਲੂ ਹੈ"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ਚਾਲੂ ਹੋਣ ਕਾਰਨ ਇਹ ਸੁਵਿਧਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ਉਪਲਬਧ ਨਹੀਂ"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। ਅਣਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। ਥਰਥਰਾਹਟ ਸੈੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 3e1e2d13f9b7..a5dbc06e2530 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Udostępnij dźwięk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Udostępnia dźwięk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"aby otworzyć ustawienia udostępniania dźwięku"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzyka i filmy z tego urządzenia będą odtwarzane przez obie pary słuchawek"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Udostępnij dźwięk"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> i <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Przełącz na: <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niedostępne, bo dzwonek jest wyciszony"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Niedostępne, włączone jest „Nie przeszkadzać”"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Niedostępne, włączone jest „Nie przeszkadzać”"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Niedostępne, bo włączony jest tryb <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Niedostępne"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Kliknij, by wyłączyć wyciszenie."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Kliknij, by włączyć wibracje. Ułatwienia dostępu mogą być wyciszone."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Kliknij, by wyciszyć. Ułatwienia dostępu mogą być wyciszone."</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index c34f2f72a53d..da192e334e31 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartilhar áudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartilhando áudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"acessar configurações de compartilhamento de áudio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"As músicas e os vídeos deste dispositivo serão reproduzidos nos dois pares de fones de ouvido"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Compartilhamento de áudio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> e <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Mudar para <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com o toque silenciado"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponível com o Não perturbe ativado"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponível com o Não perturbe ativado"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Indisponível porque o modo <xliff:g id="MODE">%s</xliff:g> está ativado"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Indisponível"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para ativar o som."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 04d3ec5c984b..127a97ea6ebc 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partilhar áudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"A partilhar áudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"aceder às definições de partilha de áudio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Os vídeos e as músicas deste dispositivo vão tocar em ambos os pares de auscultadores"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Partilhe o áudio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> e <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Mudar para <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com toque desativado"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponível porque Não incomodar está ativado"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponível com Não incomodar ativado"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Indisponível porque o modo <xliff:g id="MODE">%s</xliff:g> está ativado"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Indisponível"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para reativar o som."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para ativar a vibração. Os serviços de acessibilidade podem ser silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para desativar o som. Os serviços de acessibilidade podem ser silenciados."</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index c34f2f72a53d..da192e334e31 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartilhar áudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartilhando áudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"acessar configurações de compartilhamento de áudio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"As músicas e os vídeos deste dispositivo serão reproduzidos nos dois pares de fones de ouvido"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Compartilhamento de áudio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> e <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Mudar para <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com o toque silenciado"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponível com o Não perturbe ativado"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponível com o Não perturbe ativado"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Indisponível porque o modo <xliff:g id="MODE">%s</xliff:g> está ativado"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Indisponível"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para ativar o som."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index ba2802601df5..5c97ab579183 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Trimite audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Se permite accesul la conținutul audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"accesa setările de permitere a accesului la audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzica și videoclipurile de pe acest dispozitiv se vor reda pe ambele perechi de căști"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Permite accesul la conținutul tău audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> și <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Comută la <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponibil; soneria este dezactivată"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponibil; funcția Nu deranja e activată"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponibil; funcția Nu deranja e activată"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Indisponibil, deoarece <xliff:g id="MODE">%s</xliff:g> este activat"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Indisponibil"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Atinge pentru a activa sunetul."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Atinge pentru a seta vibrarea. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Atinge pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index bd5a5a15726a..5ae021def63e 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Отправить аудио"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Отправка аудио"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"перейти в настройки передачи аудио"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музыка и звук видео на этом устройстве будут воспроизводиться через обе пары наушников."</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Передача аудио"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> и <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Переключиться на <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно в беззвучном режиме"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недоступно при включенном режиме \"Не беспокоить\"."</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Недоступно при включенном режиме \"Не беспокоить\"."</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недоступно, так как включен режим \"<xliff:g id="MODE">%s</xliff:g>\""</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недоступно"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Нажмите, чтобы включить звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Нажмите, чтобы включить вибрацию. Специальные возможности могут прекратить работу."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Нажмите, чтобы выключить звук. Специальные возможности могут прекратить работу."</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 6d2b0be529d2..03c2ba4379ce 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ශ්රව්ය බෙදා ගන්න"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ශ්රව්ය බෙදා ගැනීම"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ශ්රව්ය බෙදා ගැනීමේ සැකසීම් ඇතුළු කරන්න"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"මෙම උපාංගයේ සංගීතය සහ වීඩියෝ හෙඩ්ෆෝන් යුගල දෙකෙහිම වාදනය වනු ඇත"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ඔබේ ශ්රව්ය බෙදා ගන්න"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> සහ <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> වෙත මාරු වන්න"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්රව්ය"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"නාදය නිහඬ කර ඇති නිසා නොලැබේ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"බාධා නොකරන්න ක්රියාත්මකව ඇති බැවින් ලද නොහැක"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"බාධා නොකරන්න ක්රියාත්මකව ඇති බැවින් ලද නොහැක"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ක්රියාත්මක නිසා ලබා ගත නොහැක"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"නොමැත"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. නිහඬ කිරීම ඉවත් කිරීමට තට්ටු කරන්න."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. කම්පනය කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 223070c5846b..ac80ae27c2d7 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Zdieľať zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zdieľa sa zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"prejsť do nastavení zdieľania zvuku"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Hudba a videá z tohto zariadenia sa budú prehrávať v oboch pároch slúchadiel"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Zdieľanie zvuku"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> a <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Prepnúť na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupné, pretože je vypnuté zvonenie"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupné, pretože je zapnutý režim bez vyrušení"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupné, zapnutý režim bez vyrušení"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nedostupné, pretože je zapnutý režim <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupné"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Klepnutím zapnite zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Klepnutím aktivujte režim vibrovania. Služby dostupnosti je možné stlmiť."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnite zvuk. Služby dostupnosti je možné stlmiť."</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index d44e70ccbf2e..f953a94dd5a5 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deli zvok"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Poteka deljenje zvoka"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"odpiranje nastavitev deljenja zvoka"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Glasba in videoposnetki v tej napravi bodo predvajani v obeh parih slušalk"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Deljenje zvoka"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> in <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Preklopi na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ni na voljo, ker je zvonjenje izklopljeno"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ni na voljo, ker je vklopljen način »Ne moti«"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ni na voljo, ker je vklopljen način »Ne moti«"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ni na voljo, ker je vklopljen način <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ni na voljo"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dotaknite se, če želite vklopiti zvok."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dotaknite se, če želite nastaviti vibriranje. V storitvah za dostopnost bo morda izklopljen zvok."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dotaknite se, če želite izklopiti zvok. V storitvah za dostopnost bo morda izklopljen zvok."</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 8787a60e1907..0166acce341b 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Ndaj audion"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioja po ndahet"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"për të hyrë te cilësimet e ndarjes së audios"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzika dhe videot e kësaj pajisjeje do të luhen në të dyja palët e kufjeve"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Ndaj audion tënde"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> dhe <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Kalo te profili \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\""</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Aktiv"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktiv • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Joaktiv"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nuk është caktuar"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Menaxho te cilësimet"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nuk ka modalitete aktive}=1{\"{mode}\" është aktiv}other{# modalitete janë aktive}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Nuk do të shqetësohesh nga tingujt dhe dridhjet, përveç alarmeve, alarmeve rikujtuese, ngjarjeve dhe telefonuesve që specifikon. Do të vazhdosh të dëgjosh çdo gjë që zgjedh të luash duke përfshirë muzikën, videot dhe lojërat."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nuk ofrohet; ziles i është hequr zëri"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nuk ofrohet; \"Mos shqetëso\" është aktiv"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nuk ofrohet; \"Mos shqetëso\" është aktiv"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nuk ofrohet sepse \"<xliff:g id="MODE">%s</xliff:g>\" është aktiv"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nuk ofrohet"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Trokit për të aktivizuar."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Trokit për ta caktuar te dridhja. Shërbimet e qasshmërisë mund të çaktivizohen."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trokit për të çaktivizuar. Shërbimet e qasshmërisë mund të çaktivizohen."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Mëso gjestet e bllokut me prekje"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigo duke përdorur tastierën dhe bllokun me prekje"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Mëso gjestet e bllokut me prekje, shkurtoret e tastierës etj."</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kthehu prapa"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Shko tek ekrani bazë"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Shiko aplikacionet e fundit"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"U krye"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kthehu prapa"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Rrëshqit shpejt majtas ose djathtas duke përdorur tre gishta në bllokun me prekje."</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bukur!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"E ke përfunduar gjestin e kthimit prapa."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Shko tek ekrani bazë"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Rrëshqit shpejt lart me tre gishta në bllokun me prekje"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Punë e shkëlqyer!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"E ke përfunduar gjestin e kalimit tek ekrani bazë"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Shiko aplikacionet e fundit"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Rrëshqit shpejt lart dhe mbaj shtypur me tre gishta në bllokun me prekje"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Punë e shkëlqyer!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Përfundove gjestin për shikimin e aplikacioneve të fundit."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Shiko të gjitha aplikacionet"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Shtyp tastin e veprimit në tastierë"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Shumë mirë!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Përfundove gjestin për shikimin e të gjitha aplikacioneve"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 599d71a685ea..d7075adc9a66 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Дели звук"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Дели се звук"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"уђите у подешавања дељења звука"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музика и видеи са овог уређаја се репродукују на пару слушалица"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Делите звук"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> и <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Пређи на <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно јер је звук искључен"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недоступно јер је укључен режим Не узнемиравај"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Недоступно јер је укључен режим Не узнемиравај"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Није доступно јер је укључено: <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недоступно"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Додирните да бисте укључили звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Додирните да бисте подесили на вибрацију. Звук услуга приступачности ће можда бити искључен."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Додирните да бисте искључили звук. Звук услуга приступачности ће можда бити искључен."</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index e1d13316b115..e03fc27b8cbf 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Dela ljud"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Delar ljud"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"öppna inställningarna för ljuddelning"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Musik och videor från den här enheten spelas upp i båda paren hörlurar"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Dela ljudet"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> och <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Byt till <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Otillgängligt eftersom ringljudet är av"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Inte tillgängligt eftersom Stör ej är aktiverat"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Inte tillgängligt eftersom Stör ej är aktiverat"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Inte tillgängligt eftersom <xliff:g id="MODE">%s</xliff:g> är på"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Inte tillgängligt"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tryck här om du vill slå på ljudet."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tryck här om du vill sätta på vibrationen. Tillgänglighetstjänster kanske inaktiveras."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryck här om du vill stänga av ljudet. Tillgänglighetstjänsterna kanske inaktiveras."</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index d7ffdd458f09..0ab6f7d5000a 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Sikiliza pamoja na wengine"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Mnasikiliza pamoja"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"uweke mipangilio ya kusikiliza pamoja"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Faili za muziki na video katika kifaa hiki zitacheza kwenye jozi zote mbili za vipokea sauti vya kichwani"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Kusikiliza maudhui yako ya sauti pamoja na wengine"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> na <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Badilisha utumie <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Halipatikani kwa sababu sauti imezimwa"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Imeshindwa kwa sababu Usinisumbue imewashwa"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Imeshindwa kwa sababu Usinisumbue imewashwa"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Haipatikani kwa sababu umewasha hali ya <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Haipatikani"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Gusa ili urejeshe."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Gusa ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Gusa ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string> diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml index fc6d20e11d3b..c661846d025f 100644 --- a/packages/SystemUI/res/values-sw600dp-land/config.xml +++ b/packages/SystemUI/res/values-sw600dp-land/config.xml @@ -27,6 +27,12 @@ <!-- Whether to use the split 2-column notification shade --> <bool name="config_use_split_notification_shade">true</bool> + <!-- The number of rows in the paginated grid QuickSettings --> + <integer name="quick_settings_paginated_grid_num_rows">3</integer> + + <!-- The number of rows in the paginated grid QuickQuickSettings --> + <integer name="quick_qs_paginated_grid_num_rows">2</integer> + <!-- The number of columns in the QuickSettings --> <integer name="quick_settings_num_columns">2</integer> diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml index 7daad1a43f73..f556b97eefc2 100644 --- a/packages/SystemUI/res/values-sw600dp-port/config.xml +++ b/packages/SystemUI/res/values-sw600dp-port/config.xml @@ -21,6 +21,12 @@ <!-- The maximum number of rows in the QuickSettings --> <integer name="quick_settings_max_rows">3</integer> + <!-- The number of rows in the paginated grid QuickSettings --> + <integer name="quick_settings_paginated_grid_num_rows">3</integer> + + <!-- The number of rows in the paginated grid QuickQuickSettings --> + <integer name="quick_qs_paginated_grid_num_rows">2</integer> + <!-- The number of columns in the QuickSettings --> <integer name="quick_settings_num_columns">3</integer> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index ba1b0ec52aec..f930d84dbfc8 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ஆடியோவைப் பகிர்"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ஆடியோ பகிரப்படுகிறது"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ஆடியோ பகிர்வு அமைப்புகளுக்குச் செல்லும்"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"இந்தச் சாதனத்தின் இசையும் வீடியோக்களும் இரண்டு ஜோடி ஹெட்ஃபோன்களிலும் பிளே ஆகும்"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ஆடியோவைப் பகிர்தல்"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>, <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>க்கு மாற்று"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"இயக்கப்பட்டுள்ளது"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ஆன் • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"முடக்கப்பட்டுள்ளது"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"அமைக்கப்படவில்லை"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"அமைப்புகளில் நிர்வகியுங்கள்"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{செயலிலுள்ள பயன்முறைகள் எதுவுமில்லை}=1{{mode} செயலில் உள்ளது}other{# பயன்முறைகள் செயலில் உள்ளன}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"அலாரங்கள், நினைவூட்டல்கள், நிகழ்வுகள் மற்றும் குறிப்பிட்ட அழைப்பாளர்களைத் தவிர்த்து, பிற ஒலிகள் மற்றும் அதிர்வுகளின் தொந்தரவு இருக்காது. எனினும், நீங்கள் எதையேனும் (இசை, வீடியோக்கள், கேம்ஸ் போன்றவை) ஒலிக்கும்படி தேர்ந்தெடுத்திருந்தால், அவை வழக்கம் போல் ஒலிக்கும்."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"\'ரிங்\' மியூட்டில் உள்ளதால் கிடைக்கவில்லை"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\'தொந்தரவு செய்ய வேண்டாம்\' ஆனில் இருப்பதால் இல்லை"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"\'தொந்தரவு செய்ய வேண்டாம்\' ஆனில் இருப்பதால் இல்லை"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> பயன்முறை இயக்கத்தில் இருப்பதால் கிடைக்கவில்லை"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"கிடைக்கவில்லை"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ஒலி இயக்க, தட்டவும்."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. அதிர்விற்கு அமைக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ஒலியடக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"டச்பேட் சைகைள் குறித்துத் தெரிந்துகொள்ளுங்கள்"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"உங்கள் டச்பேட் மற்றும் கீபோர்டைப் பயன்படுத்திச் செல்லுதல்"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"டச்பேட் சைகைகள், கீபோர்டு ஷார்ட்கட்கள் மற்றும் பலவற்றைத் தெரிந்துகொள்ளுங்கள்"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"பின்செல்"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"முகப்பிற்குச் செல்"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"சமீபத்திய ஆப்ஸைக் காட்டுதல்"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"முடிந்தது"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"பின்செல்"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"உங்கள் டச்பேடில் மூன்று விரல்களால் இடது அல்லது வலதுபுறம் ஸ்வைப் செய்யவும்"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"அருமை!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"பின்செல்வதற்கான சைகையை நிறைவுசெய்துவிட்டீர்கள்."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"முகப்பிற்குச் செல்"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்யவும்"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"அருமை!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"முகப்புக்குச் செல்வதற்கான சைகைப் பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"சமீபத்திய ஆப்ஸைக் காட்டுதல்"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"உங்கள் டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடிக்கவும்"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"அருமை!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"சமீபத்தில் பயன்படுத்திய ஆப்ஸுக்கான சைகை பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"அனைத்து ஆப்ஸையும் காட்டு"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"உங்கள் கீபோர்டில் ஆக்ஷன் பட்டனை அழுத்தவும்"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"அருமை!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"அனைத்து ஆப்ஸுக்கான சைகை பயிற்சியையும் நிறைவுசெய்துவிட்டீர்கள்"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ஹோம் கன்ட்ரோல்கள்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 8eeb1490b3ce..85973dfc1d85 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ఆడియోను షేర్ చేయండి"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ఆడియోను షేర్ చేస్తున్నారు"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ఆడియో షేరింగ్ సెట్టింగ్లను ఎంటర్ చేయండి"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ఈ పరికరం మ్యూజిక్, వీడియోలు హెడ్ఫోన్స్కు సంబంధించిన పెయిర్ల రెండింటిలోనూ ప్లే అవుతాయి"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"మీ ఆడియోను షేర్ చేయండి"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>, <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>కు మారండి"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్సెట్"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"వాల్యూమ్ మ్యూట్ అయినందున అందుబాటులో లేదు"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"అంతరాయంకలిగించవద్దు ఆన్లో ఉన్నందున అందుబాటులోలేదు"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"అంతరాయంకలిగించవద్దు ఆన్లో ఉన్నందున అందుబాటులోలేదు"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ఆన్లో ఉన్నందున అందుబాటులో లేదు"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"అందుబాటులో లేదు"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. అన్మ్యూట్ చేయడానికి నొక్కండి."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. వైబ్రేషన్కు సెట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. మ్యూట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 44a33ecaeb0c..67006b6e6273 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"แชร์เสียง"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"กำลังแชร์เสียง"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"เข้าสู่การตั้งค่าการแชร์เสียง"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"เพลงและวิดีโอของอุปกรณ์เครื่องนี้จะเล่นในหูฟังทั้ง 2 คู่"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"แชร์เสียงของคุณ"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> และ <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"เปลี่ยนเป็น <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"เปลี่ยนไม่ได้เนื่องจากปิดเสียงเรียกเข้า"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ไม่พร้อมใช้งานเนื่องจากโหมดห้ามรบกวนเปิดอยู่"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ไม่พร้อมใช้งานเนื่องจากโหมดห้ามรบกวนเปิดอยู่"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"ใช้งานไม่ได้เนื่องจากเปิด<xliff:g id="MODE">%s</xliff:g>อยู่"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ไม่พร้อมใช้งาน"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s แตะเพื่อเปิดเสียง"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s แตะเพื่อตั้งค่าให้สั่น อาจมีการปิดเสียงบริการการเข้าถึง"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s แตะเพื่อปิดเสียง อาจมีการปิดเสียงบริการการเข้าถึง"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index c782dd412196..e41c53f2fa5c 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Ibahagi ang audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ibinabahagi ang audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"pumasok sa mga setting sa pag-share ng audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Magpe-play ang musika at mga video ng device na ito sa parehong pares ng headphones"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Ibahagi ang iyong audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> at <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Lumipat sa <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Hindi available dahil naka-mute ang ring"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Hindi available dahil naka-on ang Huwag Istorbohin"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Hindi available dahil naka-on ang Huwag Istorbohin"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Hindi available dahil naka-on ang <xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Hindi available"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. I-tap upang i-unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. I-tap upang itakda na mag-vibrate. Maaaring i-mute ang mga serbisyo sa Accessibility."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. I-tap upang i-mute. Maaaring i-mute ang mga serbisyo sa Accessibility."</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index ad543cfc72ce..1dab64adc32a 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Sesi paylaş"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ses paylaşılıyor"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"Ses paylaşımı ayarlarına gitmek için"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Bu cihazdaki müzikler ve videolar iki kulaklıkta da oynatılacak"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Sesi paylaşın"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ve <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> cihazına geç"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Açık"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Açık • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Kapalı"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Ayarlanmadı"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Ayarlarda yönet"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Etkin mod yok}=1{{mode} etkin}other{# mod etkin}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Alarmlar, hatırlatıcılar, etkinlikler ve sizin seçtiğiniz kişilerden gelen çağrılar dışında hiçbir ses ve titreşimle rahatsız edilmeyeceksiniz. O sırada çaldığınız müzik, seyrettiğiniz video ya da oynadığınız oyunların sesini duymaya devam edeceksiniz."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Zil sesi kapatıldığı için kullanılamıyor"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Rahatsız Etmeyin açık olduğu için kullanılamıyor"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Rahatsız Etmeyin açık olduğu için kullanılamıyor"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> açık olduğu için kullanılamıyor"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Yok"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sesi açmak için dokunun."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Titreşime ayarlamak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sesi kapatmak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Dokunmatik alan hareketlerini öğrenin"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klavyenizi ve dokunmatik alanınızı kullanarak gezinin"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Dokunmatik alan hareketlerini, klavye kısayollarını ve daha fazlasını öğrenin"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Geri dön"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ana sayfaya git"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son uygulamaları görüntüle"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Bitti"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri dön"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Dokunmatik alanda üç parmağınızla sola veya sağa kaydırın"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Güzel!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Geri dön hareketini tamamladınız."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ana sayfaya gidin"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Dokunmatik alanda üç parmağınızla yukarı kaydırın"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Tebrikler!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ana ekrana git hareketini tamamladınız"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Son uygulamaları görüntüle"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Dokunmatik alanda üç parmağınızla yukarı doğru kaydırıp basılı tutun"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Tebrikler!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Son uygulamaları görüntüleme hareketini tamamladınız."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Tüm uygulamaları göster"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klavyenizde eylem tuşuna basın"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Tebrikler!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Tüm uygulamaları görüntüleme hareketini tamamladınız"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 0d664e3f16bb..fcdccdb7e77d 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Поділитись аудіо"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Надсилання аудіо"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"відкрити налаштування надсилання аудіо"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музика й відео із цього пристрою відтворюватимуться на обох парах навушників"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Поділитися аудіо"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"Пристрої \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\" і \"<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>\""</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Перемкнути на \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\""</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string> @@ -440,8 +444,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Увімкнено"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Увімк. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Вимкнено"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Не налаштовано"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Керувати в налаштуваннях"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Немає активних режимів}=1{Активовано режим \"{mode}\"}one{Активовано # режим}few{Активовано # режими}many{Активовано # режимів}other{Активовано # режиму}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Ви отримуватиме звукові та вібросигнали лише для вибраних будильників, нагадувань, подій і абонентів. Однак ви чутимете все, що захочете відтворити, зокрема музику, відео й ігри."</string> @@ -673,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно: звук дзвінків вимкнено"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недоступно: увімкнено режим \"Не турбувати\""</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Недоступно: увімкнено режим \"Не турбувати\""</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недоступно, оскільки ввімкнено режим \"<xliff:g id="MODE">%s</xliff:g>\""</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недоступно"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Торкніться, щоб увімкнути звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Торкніться, щоб налаштувати вібросигнал. Спеціальні можливості може бути вимкнено."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Торкніться, щоб вимкнути звук. Спеціальні можливості може бути вимкнено."</string> @@ -1410,38 +1411,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Жести для сенсорної панелі: докладніше"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навігація за допомогою клавіатури й сенсорної панелі"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Жести для сенсорної панелі, комбінації клавіш тощо: докладніше"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Перейти на головний екран"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Переглянути нещодавні додатки"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Проведіть трьома пальцями вліво чи вправо по сенсорній панелі"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Чудово!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ви виконали жест \"Назад\"."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Перейти на головний екран"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Проведіть трьома пальцями вгору на сенсорній панелі"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Чудово!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ви виконали жест переходу на головний екран"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Переглянути нещодавні додатки"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Проведіть трьома пальцями вгору й утримуйте їх на сенсорній панелі"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Чудово!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ви виконали жест для перегляду нещодавно відкритих додатків."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Переглянути всі додатки"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натисніть клавішу дії на клавіатурі"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Чудово!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ви виконали жест для перегляду всіх додатків"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Підсвічування клавіатури"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Рівень %1$d з %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Автоматизація дому"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index b1ddfc98d308..a091d305b924 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"آڈیو کا اشتراک کریں"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"آڈیو کا اشتراک ہو رہا ہے"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"آڈیو کے اشتراک کی ترتیبات درج کریں"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"اس آلہ کی موسیقی اور ویڈیوز ہیڈ فونز کے دونوں جوڑے پر چلیں گی"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"اپنی آڈیو کا اشتراک کریں"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> اور <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> پر سوئچ کریں"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"دستیاب نہیں ہے کیونکہ رنگ خاموش ہے"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"غیر دستیاب ہے کیونکہ ڈسٹرب نہ کریں آن ہے"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"غیر دستیاب ہے کیونکہ ڈسٹرب نہ کریں آن ہے"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> کے آن ہونے کی وجہ سے غیر دستیاب ہے"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"غیر دستیاب ہے"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s۔ آواز چالو کرنے کیلئے تھپتھپائیں۔"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s۔ ارتعاش پر سیٹ کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 6d597bd99609..51dcb29d3128 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audioni ulashish"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio ulashuvi yoniq"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"audio ulashuv sozlamalarini kiritish"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Bu qurilmadagi musiqa va videolar quloqliklarning ikkala tomonida ijro etiladi"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Audioni ulashish"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> va <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Bunga almashish: <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Jiringlash ovozsizligi uchun ishlamaydi"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Bezovta qilinmasin yoniqligi sababli ishlamaydi"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Bezovta qilinmasin yoniqligi sababli ishlamaydi"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> yoniqligi uchun ishlamaydi"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ishlamaydi"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ovozini yoqish uchun ustiga bosing."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tebranishni yoqish uchun ustiga bosing. Qulayliklar ishlamasligi mumkin."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ovozini o‘chirish uchun ustiga bosing. Qulayliklar ishlamasligi mumkin."</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 7e1f6e7780b6..98fc4679178a 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Chia sẻ âm thanh"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Đang chia sẻ âm thanh"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"mở chế độ cài đặt chia sẻ âm thanh"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Nhạc và video trên thiết bị này sẽ phát trên cả hai bộ tai nghe"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Chia sẻ âm thanh của bạn"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> và <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Chuyển sang <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Không hoạt động vì chuông bị tắt"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Bị tắt vì đang bật chế độ Không làm phiền"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Bị tắt vì đang bật chế độ Không làm phiền"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Không dùng được vì <xliff:g id="MODE">%s</xliff:g> đang bật"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Không dùng được"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Nhấn để bật tiếng."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Nhấn để đặt chế độ rung. Bạn có thể tắt tiếng dịch vụ trợ năng."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Nhấn để tắt tiếng. Bạn có thể tắt tiếng dịch vụ trợ năng."</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 0aa679821511..cf80402a89d8 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音频"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音频"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"进入音频分享设置"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"此设备上的音乐和视频会同时通过两副耳机播放"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"分享您的音频"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"“<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>”和“<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>”"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"切换到“<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>”"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"该功能无法使用,因为铃声被静音"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"“勿扰”模式已开启,因此无法调整音量"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"“勿扰”模式已开启,因此无法调整音量"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"不可用,因为已开启<xliff:g id="MODE">%s</xliff:g>"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"不可用"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。点按即可取消静音。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。点按即可设为振动,但可能会同时将无障碍服务设为静音。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。点按即可设为静音,但可能会同时将无障碍服务设为静音。"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 4ff574e27ef2..39cf982dd36e 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音訊"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音訊"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"輸入音訊分享功能設定"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"兩對耳機都會播放此裝置的音樂和影片"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"分享音訊"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"「<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>」和「<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>」"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"切換至「<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>」"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"鈴聲已設定為靜音,因此無法使用"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"「請勿騷擾」已開啟,因此無法使用"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"「請勿騷擾」已開啟,因此無法使用"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g>已開啟,因此無法使用"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"未有提供"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。輕按即可取消靜音。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。輕按即可設為震動。無障礙功能服務可能已經設為靜音。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕按即可設為靜音。無障礙功能服務可能已經設為靜音。"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 9fea9bc5b9f1..cbde8c3d3e8d 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音訊"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音訊"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"進入音訊分享設定"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"這部裝置會同時在兩對耳機上播放音樂和影片"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"分享音訊"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"「<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>」和「<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>」"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"切換至「<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>」"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"鈴聲已設為靜音,因此無法使用"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"「零打擾」模式已開啟,因此無法調整音量"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"「零打擾」模式已開啟,因此無法調整音量"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"「<xliff:g id="MODE">%s</xliff:g>」功能已開啟,因此無法調整音量"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"無法使用"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。輕觸即可取消靜音。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。輕觸即可設為震動,但系統可能會將無障礙服務一併設為靜音。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕觸即可設為靜音,但系統可能會將無障礙服務一併設為靜音。"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 9ff3e5dee7c6..2e668c5f732a 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -312,6 +312,10 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Yabelana ngomsindo"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Yabelana ngomsindo"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"faka amasethingi okwabelana ngokuqoshiwe"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Umculo namavidiyo ale divayisi azodlala kukho kokubili ukubhangqwa kwamaheadphone"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Yabelana ngomsindo wakho"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"I-<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ne-<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Shintshela ku-<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string> @@ -672,10 +676,8 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ayitholakali ngoba ukukhala kuthulisiwe"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ayitholakali ngoba okuthi Ungaphazamisi kuvuliwe"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ayitholakali ngoba okuthi Ungaphazamisi kuvuliwe"</string> - <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> - <skip /> - <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> - <skip /> + <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ayitholakali ngoba i-<xliff:g id="MODE">%s</xliff:g> ivuliwe"</string> + <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ayitholakali"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Thepha ukuze ususe ukuthula."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Thepha ukuze usethe ukudlidliza. Amasevisi okufinyelela angathuliswa."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Thepha ukuze uthulise. Amasevisi okufinyelela angathuliswa."</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 6f94f9e2a216..f96a0b95945d 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -70,12 +70,21 @@ <!-- The number of rows in the QuickSettings --> <integer name="quick_settings_max_rows">4</integer> + <!-- The number of rows in the paginated grid QuickSettings --> + <integer name="quick_settings_paginated_grid_num_rows">4</integer> + + <!-- The number of rows in the paginated grid QuickQuickSettings --> + <integer name="quick_qs_paginated_grid_num_rows">2</integer> + <!-- The number of columns in the infinite grid QuickSettings --> <integer name="quick_settings_infinite_grid_num_columns">4</integer> <!-- The number of columns in the Dual Shade QuickSettings --> <integer name="quick_settings_dual_shade_num_columns">4</integer> + <!-- The number of columns in the Split Shade QuickSettings --> + <integer name="quick_settings_split_shade_num_columns">4</integer> + <!-- Override column number for quick settings. For now, this value has effect only when flag lockscreen.enable_landscape is enabled. TODO (b/293252410) - change this comment/resource when flag is enabled --> @@ -128,17 +137,19 @@ <!-- Use collapsed layout for media player in landscape QQS --> <bool name="config_quickSettingsMediaLandscapeCollapsed">true</bool> - <!-- For hearing devices related tool list. Need to be in ComponentName format (package/class). - Should be activity to be launched. - Already contains tool that holds intent: "com.android.settings.action.live_caption". - Maximum number is 3. --> + <!-- Hearing devices related tool list. Each entry must be in ComponentName format + (package/class), indicating the specific application component to launch. + Already contains tool that handles intent: "com.android.settings.action.live_caption" by + default. You can add up to 2 additional related tools. --> <string-array name="config_quickSettingsHearingDevicesRelatedToolName" translatable="false"> </string-array> - <!-- The drawable resource names. If provided, it will replace the corresponding icons in - config_quickSettingsHearingDevicesRelatedToolName. Can be empty to use original icons. - Already contains tool that holds intent: "com.android.settings.action.live_caption". - Maximum number is 3. --> + <!-- Hearing devices related tool icon list. Provide drawable resource names in the same order + as the component names in config_quickSettingsHearingDevicesRelatedToolName. The icons + should be monochrome and will be tinted according to the system's material color. Ensure + the number of icon resources matches the number of components specified in + config_quickSettingsHearingDevicesRelatedToolName. If this array is empty or the sizes + don't match, the original application icons will be used. --> <string-array name="config_quickSettingsHearingDevicesRelatedToolIcon" translatable="false"> </string-array> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 6c8a7403953e..209a971814f4 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1798,8 +1798,6 @@ <dimen name="hearing_devices_preset_spinner_text_padding_vertical">15dp</dimen> <dimen name="hearing_devices_preset_spinner_arrow_size">24dp</dimen> <dimen name="hearing_devices_preset_spinner_background_radius">28dp</dimen> - <dimen name="hearing_devices_tool_icon_frame_width">94dp</dimen> - <dimen name="hearing_devices_tool_icon_frame_height">64dp</dimen> <dimen name="hearing_devices_tool_icon_size">28dp</dimen> <!-- Height percentage of the parent container occupied by the communal view --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index e1f25f99cc98..7cebac2feaba 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -70,6 +70,20 @@ <item name="android:fontWeight">700</item> </style> + <style name="StatusBar" /> + <style name="StatusBar.Chip" /> + + <style name="StatusBar.Chip.Text"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:singleLine">true</item> + <item name="android:gravity">center|start</item> + <item name="android:paddingStart">@dimen/ongoing_activity_chip_icon_text_padding</item> + <item name="android:textAppearance">@android:style/TextAppearance.Material.Small</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:textColor">?android:attr/colorPrimary</item> + </style> + <style name="Chipbar" /> <style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title"> @@ -677,10 +691,12 @@ <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings"> <item name="android:windowActionBar">false</item> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> <item name="preferenceTheme">@style/TunerPreferenceTheme</item> </style> <style name="TunerPreferenceTheme" parent="@style/PreferenceThemeOverlay.SettingsBase"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> <style name="TextAppearance.NotificationInfo.Confirmation"> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index 1e668b84cdc6..9a264c6ca414 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -28,6 +28,8 @@ import android.util.ArraySet; import android.util.Log; import android.widget.Toast; +import androidx.annotation.Nullable; + import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; @@ -62,7 +64,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage public PluginManagerImpl(Context context, PluginActionManager.Factory actionManagerFactory, boolean debuggable, - UncaughtExceptionPreHandlerManager preHandlerManager, + @Nullable UncaughtExceptionPreHandlerManager preHandlerManager, PluginEnabler pluginEnabler, PluginPrefs pluginPrefs, List<String> privilegedPlugins) { @@ -73,7 +75,9 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage mPluginPrefs = pluginPrefs; mPluginEnabler = pluginEnabler; - preHandlerManager.registerHandler(new PluginExceptionHandler()); + if (preHandlerManager != null) { + preHandlerManager.registerHandler(new PluginExceptionHandler()); + } } public boolean isDebuggable() { diff --git a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt index b792db354b36..306d68217e50 100644 --- a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt +++ b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt @@ -17,6 +17,7 @@ package com.android.keyguard import android.view.MotionEvent +import android.view.View import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.res.R @@ -34,11 +35,10 @@ import javax.inject.Inject @SysUISingleton class EmptyLockIconViewController @Inject -constructor( - private val keyguardRootView: Lazy<KeyguardRootView>, -) : LockIconViewController { +constructor(private val keyguardRootView: Lazy<KeyguardRootView>) : LockIconViewController { private val deviceEntryIconViewId = R.id.device_entry_icon_view - override fun setLockIconView(lockIconView: LockIconView) { + + override fun setLockIconView(lockIconView: View) { // no-op } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 7cba845460ca..ec0f582dcb47 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -22,8 +22,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.Flags.smartspaceRelocateToBottom; -import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; -import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.annotation.Nullable; import android.database.ContentObserver; @@ -36,13 +34,11 @@ import android.widget.FrameLayout; import android.widget.LinearLayout; import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; import com.android.systemui.Dumpable; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.MigrateClocksToBlueprint; import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor; @@ -71,7 +67,6 @@ import kotlinx.coroutines.DisposableHandle; import java.io.PrintWriter; import java.util.Locale; import java.util.concurrent.Executor; -import java.util.function.Consumer; import javax.inject.Inject; @@ -114,8 +109,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private boolean mShownOnSecondaryDisplay = false; private boolean mOnlyClock = false; - private boolean mIsActiveDreamLockscreenHosted = false; - private final FeatureFlagsClassic mFeatureFlags; private KeyguardInteractor mKeyguardInteractor; private KeyguardClockInteractor mKeyguardClockInteractor; private final DelayableExecutor mUiExecutor; @@ -124,15 +117,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private DisposableHandle mAodIconsBindHandle; @Nullable private NotificationIconContainer mAodIconContainer; - @VisibleForTesting - final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback = - (Boolean isLockscreenHosted) -> { - if (mIsActiveDreamLockscreenHosted == isLockscreenHosted) { - return; - } - mIsActiveDreamLockscreenHosted = isLockscreenHosted; - updateKeyguardStatusAreaVisibility(); - }; private final ContentObserver mDoubleLineClockObserver = new ContentObserver(null) { @Override public void onChange(boolean change) { @@ -173,7 +157,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS @KeyguardClockLog LogBuffer logBuffer, KeyguardInteractor keyguardInteractor, KeyguardClockInteractor keyguardClockInteractor, - FeatureFlagsClassic featureFlags, InWindowLauncherUnlockAnimationManager inWindowLauncherUnlockAnimationManager) { super(keyguardClockSwitch); mStatusBarStateController = statusBarStateController; @@ -189,7 +172,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mClockEventController = clockEventController; mLogBuffer = logBuffer; mView.setLogBuffer(mLogBuffer); - mFeatureFlags = featureFlags; mKeyguardInteractor = keyguardInteractor; mKeyguardClockInteractor = keyguardClockInteractor; mInWindowLauncherUnlockAnimationManager = inWindowLauncherUnlockAnimationManager; @@ -251,12 +233,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mDumpManager.unregisterDumpable(getClass().getSimpleName()); // unregister previous mDumpManager.registerDumpable(getClass().getSimpleName(), this); } - - if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { - mStatusArea = mView.findViewById(R.id.keyguard_status_area); - collectFlow(mStatusArea, mKeyguardInteractor.isActiveDreamLockscreenHosted(), - mIsActiveDreamLockscreenHostedCallback); - } } public KeyguardClockSwitch getView() { @@ -668,15 +644,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } } - private void updateKeyguardStatusAreaVisibility() { - if (mStatusArea != null) { - mUiExecutor.execute(() -> { - mStatusArea.setVisibility( - mIsActiveDreamLockscreenHosted ? View.INVISIBLE : View.VISIBLE); - }); - } - } - /** * Sets the clipChildren property on relevant views, to allow the smartspace to draw out of * bounds during the unlock transition. diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java deleted file mode 100644 index 03b13fe47c10..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java +++ /dev/null @@ -1,843 +0,0 @@ -/* - * Copyright (C) 2020 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.keyguard; - -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; -import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; - -import static com.android.keyguard.LockIconView.ICON_FINGERPRINT; -import static com.android.keyguard.LockIconView.ICON_LOCK; -import static com.android.keyguard.LockIconView.ICON_UNLOCK; -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; -import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; -import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; -import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.Rect; -import android.hardware.biometrics.BiometricAuthenticator; -import android.hardware.biometrics.BiometricSourceType; -import android.os.VibrationAttributes; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.MathUtils; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.WindowInsets; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityNodeInfo; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; - -import com.android.systemui.Dumpable; -import com.android.systemui.biometrics.AuthController; -import com.android.systemui.biometrics.AuthRippleController; -import com.android.systemui.biometrics.UdfpsController; -import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; -import com.android.systemui.keyguard.KeyguardBottomAreaRefactor; -import com.android.systemui.keyguard.MigrateClocksToBlueprint; -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.shared.model.KeyguardState; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.res.R; -import com.android.systemui.scene.shared.flag.SceneContainerFlag; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.concurrency.DelayableExecutor; - -import dagger.Lazy; - -import kotlinx.coroutines.ExperimentalCoroutinesApi; - -import java.io.PrintWriter; -import java.util.Objects; -import java.util.function.Consumer; - -import javax.inject.Inject; - -/** - * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen. - * - * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock - * icon will show a set distance from the bottom of the device. - */ -@SysUISingleton -public class LegacyLockIconViewController implements Dumpable, LockIconViewController { - private static final String TAG = "LockIconViewController"; - private static final float sDefaultDensity = - (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT; - private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36); - private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = - VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); - - private static final long FADE_OUT_DURATION_MS = 250L; - - private final long mLongPressTimeout; - @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @NonNull private final KeyguardViewController mKeyguardViewController; - @NonNull private final StatusBarStateController mStatusBarStateController; - @NonNull private final KeyguardStateController mKeyguardStateController; - @NonNull private final FalsingManager mFalsingManager; - @NonNull private final AuthController mAuthController; - @NonNull private final AccessibilityManager mAccessibilityManager; - @NonNull private final ConfigurationController mConfigurationController; - @NonNull private final DelayableExecutor mExecutor; - private boolean mUdfpsEnrolled; - private Resources mResources; - private Context mContext; - @NonNull private CharSequence mUnlockedLabel; - @NonNull private CharSequence mLockedLabel; - @NonNull private final VibratorHelper mVibrator; - @Nullable private final AuthRippleController mAuthRippleController; - @NonNull private final FeatureFlags mFeatureFlags; - @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; - @NonNull private final KeyguardTransitionInteractor mTransitionInteractor; - @NonNull private final KeyguardInteractor mKeyguardInteractor; - @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate; - @NonNull private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor; - - // Tracks the velocity of a touch to help filter out the touches that move too fast. - private VelocityTracker mVelocityTracker; - // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active. - private int mActivePointerId = -1; - - private boolean mIsDozing; - private boolean mIsActiveDreamLockscreenHosted; - private boolean mIsBouncerShowing; - private boolean mRunningFPS; - private boolean mCanDismissLockScreen; - private int mStatusBarState; - private boolean mIsKeyguardShowing; - private Runnable mLongPressCancelRunnable; - - private boolean mUdfpsSupported; - private float mHeightPixels; - private float mWidthPixels; - private int mBottomPaddingPx; - private int mDefaultPaddingPx; - - private boolean mShowUnlockIcon; - private boolean mShowLockIcon; - - // for udfps when strong auth is required or unlocked on AOD - private boolean mShowAodLockIcon; - private boolean mShowAodUnlockedIcon; - private final int mMaxBurnInOffsetX; - private final int mMaxBurnInOffsetY; - private float mInterpolatedDarkAmount; - - private boolean mDownDetected; - private final Rect mSensorTouchLocation = new Rect(); - private LockIconView mView; - - @VisibleForTesting - final Consumer<Float> mDozeTransitionCallback = (Float value) -> { - mInterpolatedDarkAmount = value; - mView.setDozeAmount(value); - updateBurnInOffsets(); - }; - - @VisibleForTesting - final Consumer<Boolean> mIsDozingCallback = (Boolean isDozing) -> { - mIsDozing = isDozing; - updateBurnInOffsets(); - updateVisibility(); - }; - - @VisibleForTesting - final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback = - (Boolean isLockscreenHosted) -> { - mIsActiveDreamLockscreenHosted = isLockscreenHosted; - updateVisibility(); - }; - - @Inject - public LegacyLockIconViewController( - @NonNull StatusBarStateController statusBarStateController, - @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, - @NonNull KeyguardViewController keyguardViewController, - @NonNull KeyguardStateController keyguardStateController, - @NonNull FalsingManager falsingManager, - @NonNull AuthController authController, - @NonNull DumpManager dumpManager, - @NonNull AccessibilityManager accessibilityManager, - @NonNull ConfigurationController configurationController, - @NonNull @Main DelayableExecutor executor, - @NonNull VibratorHelper vibrator, - @Nullable AuthRippleController authRippleController, - @NonNull @Main Resources resources, - @NonNull KeyguardTransitionInteractor transitionInteractor, - @NonNull KeyguardInteractor keyguardInteractor, - @NonNull FeatureFlags featureFlags, - PrimaryBouncerInteractor primaryBouncerInteractor, - Context context, - Lazy<DeviceEntryInteractor> deviceEntryInteractor - ) { - mStatusBarStateController = statusBarStateController; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mAuthController = authController; - mKeyguardViewController = keyguardViewController; - mKeyguardStateController = keyguardStateController; - mFalsingManager = falsingManager; - mAccessibilityManager = accessibilityManager; - mConfigurationController = configurationController; - mExecutor = executor; - mVibrator = vibrator; - mAuthRippleController = authRippleController; - mTransitionInteractor = transitionInteractor; - mKeyguardInteractor = keyguardInteractor; - mFeatureFlags = featureFlags; - mPrimaryBouncerInteractor = primaryBouncerInteractor; - - mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); - mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); - mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button); - mLockedLabel = resources.getString(R.string.accessibility_lock_icon); - mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress); - dumpManager.registerDumpable(TAG, this); - mResources = resources; - mContext = context; - mDeviceEntryInteractor = deviceEntryInteractor; - - mAccessibilityDelegate = new View.AccessibilityDelegate() { - private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint = - new AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfoCompat.ACTION_CLICK, - mResources.getString(R.string.accessibility_authenticate_hint)); - private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint = - new AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfoCompat.ACTION_CLICK, - mResources.getString(R.string.accessibility_enter_hint)); - public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(v, info); - if (isActionable()) { - if (mShowLockIcon) { - info.addAction(mAccessibilityAuthenticateHint); - } else if (mShowUnlockIcon) { - info.addAction(mAccessibilityEnterHint); - } - } - } - }; - } - - /** Sets the LockIconView to the controller and rebinds any that depend on it. */ - @SuppressLint("ClickableViewAccessibility") - @Override - public void setLockIconView(LockIconView lockIconView) { - mView = lockIconView; - mView.setAccessibilityDelegate(mAccessibilityDelegate); - - if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { - collectFlow(mView, mTransitionInteractor.transitionValue(KeyguardState.AOD), - mDozeTransitionCallback); - collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback); - } - - if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { - collectFlow(mView, mKeyguardInteractor.isActiveDreamLockscreenHosted(), - mIsActiveDreamLockscreenHostedCallback); - } - - updateIsUdfpsEnrolled(); - updateConfiguration(); - updateKeyguardShowing(); - - mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); - mIsDozing = mStatusBarStateController.isDozing(); - mInterpolatedDarkAmount = mStatusBarStateController.getDozeAmount(); - mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning(); - mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen(); - mStatusBarState = mStatusBarStateController.getState(); - - updateColors(); - mDownDetected = false; - updateBurnInOffsets(); - updateVisibility(); - - updateAccessibility(); - - lockIconView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View view) { - registerCallbacks(); - } - - @Override - public void onViewDetachedFromWindow(View view) { - unregisterCallbacks(); - } - }); - - if (lockIconView.isAttachedToWindow()) { - registerCallbacks(); - } - - lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent)); - } - - private void registerCallbacks() { - mConfigurationController.addCallback(mConfigurationListener); - mAuthController.addCallback(mAuthControllerCallback); - mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); - mStatusBarStateController.addCallback(mStatusBarStateListener); - mKeyguardStateController.addCallback(mKeyguardStateCallback); - mAccessibilityManager.addAccessibilityStateChangeListener( - mAccessibilityStateChangeListener); - - } - - private void unregisterCallbacks() { - mAuthController.removeCallback(mAuthControllerCallback); - mConfigurationController.removeCallback(mConfigurationListener); - mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); - mStatusBarStateController.removeCallback(mStatusBarStateListener); - mKeyguardStateController.removeCallback(mKeyguardStateCallback); - mAccessibilityManager.removeAccessibilityStateChangeListener( - mAccessibilityStateChangeListener); - - } - - private void updateAccessibility() { - if (mAccessibilityManager.isEnabled()) { - mView.setOnClickListener(mA11yClickListener); - } else { - mView.setOnClickListener(null); - } - } - - @Override - public float getTop() { - return mView.getLocationTop(); - } - - @Override - public float getBottom() { - return mView.getLocationBottom(); - } - - private void updateVisibility() { - if (!mIsKeyguardShowing && !mIsDozing) { - mView.setVisibility(View.INVISIBLE); - return; - } - - if (mIsKeyguardShowing && mIsActiveDreamLockscreenHosted) { - mView.setVisibility(View.INVISIBLE); - return; - } - - boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon - && !mShowAodUnlockedIcon && !mShowAodLockIcon; - mShowLockIcon = !mCanDismissLockScreen && isLockScreen() - && (!mUdfpsEnrolled || !mRunningFPS); - mShowUnlockIcon = mCanDismissLockScreen && isLockScreen(); - mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen; - mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen; - - final CharSequence prevContentDescription = mView.getContentDescription(); - if (mShowLockIcon) { - if (wasShowingFpIcon) { - // fp icon was shown by UdfpsView, and now we still want to animate the transition - // in this drawable - mView.updateIcon(ICON_FINGERPRINT, false); - } - mView.updateIcon(ICON_LOCK, false); - mView.setContentDescription(mLockedLabel); - mView.setVisibility(View.VISIBLE); - } else if (mShowUnlockIcon) { - if (wasShowingFpIcon) { - // fp icon was shown by UdfpsView, and now we still want to animate the transition - // in this drawable - mView.updateIcon(ICON_FINGERPRINT, false); - } - mView.updateIcon(ICON_UNLOCK, false); - mView.setContentDescription(mUnlockedLabel); - mView.setVisibility(View.VISIBLE); - } else if (mShowAodUnlockedIcon) { - mView.updateIcon(ICON_UNLOCK, true); - mView.setContentDescription(mUnlockedLabel); - mView.setVisibility(View.VISIBLE); - } else if (mShowAodLockIcon) { - mView.updateIcon(ICON_LOCK, true); - mView.setContentDescription(mLockedLabel); - mView.setVisibility(View.VISIBLE); - } else { - mView.clearIcon(); - mView.setVisibility(View.INVISIBLE); - mView.setContentDescription(null); - } - - boolean accessibilityEnabled = - !mPrimaryBouncerInteractor.isAnimatingAway() && mView.isVisibleToUser(); - mView.setImportantForAccessibility( - accessibilityEnabled ? View.IMPORTANT_FOR_ACCESSIBILITY_YES - : View.IMPORTANT_FOR_ACCESSIBILITY_NO); - - if (!Objects.equals(prevContentDescription, mView.getContentDescription()) - && mView.getContentDescription() != null && accessibilityEnabled) { - mView.announceForAccessibility(mView.getContentDescription()); - } - } - - private boolean isLockScreen() { - return !mIsDozing - && !mIsBouncerShowing - && mStatusBarState == StatusBarState.KEYGUARD; - } - - private void updateKeyguardShowing() { - mIsKeyguardShowing = mKeyguardStateController.isShowing() - && !mKeyguardStateController.isKeyguardGoingAway(); - } - - private void updateColors() { - mView.updateColorAndBackgroundVisibility(); - } - - private void updateConfiguration() { - WindowManager windowManager = mContext.getSystemService(WindowManager.class); - Rect bounds = windowManager.getCurrentWindowMetrics().getBounds(); - mWidthPixels = bounds.right; - if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) { - // Assumed to be initially neglected as there are no left or right insets in portrait - // However, on landscape, these insets need to included when calculating the midpoint - WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets(); - mWidthPixels -= insets.getSystemWindowInsetLeft() + insets.getSystemWindowInsetRight(); - } - mHeightPixels = bounds.bottom; - mBottomPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom); - mDefaultPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_padding); - mUnlockedLabel = mResources.getString( - R.string.accessibility_unlock_button); - mLockedLabel = mResources.getString(R.string.accessibility_lock_icon); - updateLockIconLocation(); - } - - private void updateLockIconLocation() { - final float scaleFactor = mAuthController.getScaleFactor(); - final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor); - if (KeyguardBottomAreaRefactor.isEnabled() || MigrateClocksToBlueprint.isEnabled()) { - // positioning in this case is handled by [DefaultDeviceEntrySection] - mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding, - scaledPadding); - } else { - if (mUdfpsSupported) { - mView.setCenterLocation(mAuthController.getUdfpsLocation(), - mAuthController.getUdfpsRadius(), scaledPadding); - } else { - mView.setCenterLocation( - new Point((int) mWidthPixels / 2, - (int) (mHeightPixels - - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))), - sLockIconRadiusPx * scaleFactor, scaledPadding); - } - } - } - - @Override - public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - pw.println("mUdfpsSupported: " + mUdfpsSupported); - pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled); - pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing); - pw.println(); - pw.println(" mShowUnlockIcon: " + mShowUnlockIcon); - pw.println(" mShowLockIcon: " + mShowLockIcon); - pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon); - pw.println(); - pw.println(" mIsDozing: " + mIsDozing); - pw.println(" isFlagEnabled(DOZING_MIGRATION_1): " - + mFeatureFlags.isEnabled(DOZING_MIGRATION_1)); - pw.println(" mIsBouncerShowing: " + mIsBouncerShowing); - pw.println(" mRunningFPS: " + mRunningFPS); - pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen); - pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState)); - pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount); - pw.println(" mSensorTouchLocation: " + mSensorTouchLocation); - pw.println(" mDefaultPaddingPx: " + mDefaultPaddingPx); - pw.println(" mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted); - - if (mView != null) { - mView.dump(pw, args); - } - } - - /** Every minute, update the aod icon's burn in offset */ - @Override - public void dozeTimeTick() { - updateBurnInOffsets(); - } - - private void updateBurnInOffsets() { - float offsetX = MathUtils.lerp(0f, - getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */) - - mMaxBurnInOffsetX, mInterpolatedDarkAmount); - float offsetY = MathUtils.lerp(0f, - getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) - - mMaxBurnInOffsetY, mInterpolatedDarkAmount); - - mView.setTranslationX(offsetX); - mView.setTranslationY(offsetY); - } - - private void updateIsUdfpsEnrolled() { - boolean wasUdfpsSupported = mUdfpsSupported; - boolean wasUdfpsEnrolled = mUdfpsEnrolled; - - mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported(); - mView.setUseBackground(mUdfpsSupported); - - mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled(); - if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) { - updateVisibility(); - } - } - - private StatusBarStateController.StateListener mStatusBarStateListener = - new StatusBarStateController.StateListener() { - @Override - public void onDozeAmountChanged(float linear, float eased) { - if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { - mInterpolatedDarkAmount = eased; - mView.setDozeAmount(eased); - updateBurnInOffsets(); - } - } - - @Override - public void onDozingChanged(boolean isDozing) { - if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { - mIsDozing = isDozing; - updateBurnInOffsets(); - updateVisibility(); - } - } - - @Override - public void onStateChanged(int statusBarState) { - mStatusBarState = statusBarState; - updateVisibility(); - } - }; - - private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onKeyguardBouncerStateChanged(boolean bouncer) { - mIsBouncerShowing = bouncer; - updateVisibility(); - } - - @Override - public void onBiometricRunningStateChanged(boolean running, - BiometricSourceType biometricSourceType) { - final boolean wasRunningFps = mRunningFPS; - - if (biometricSourceType == FINGERPRINT) { - mRunningFPS = running; - } - - if (wasRunningFps != mRunningFPS) { - updateVisibility(); - } - } - }; - - private final KeyguardStateController.Callback mKeyguardStateCallback = - new KeyguardStateController.Callback() { - @Override - public void onUnlockedChanged() { - mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen(); - updateKeyguardShowing(); - updateVisibility(); - } - - @Override - public void onKeyguardShowingChanged() { - // Reset values in case biometrics were removed (ie: pin/pattern/password => swipe). - // If biometrics were removed, local vars mCanDismissLockScreen and - // mUserUnlockedWithBiometric may not be updated. - mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen(); - - // reset mIsBouncerShowing state in case it was preemptively set - // onLongPress - mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); - - updateKeyguardShowing(); - updateVisibility(); - } - - @Override - public void onKeyguardFadingAwayChanged() { - updateKeyguardShowing(); - updateVisibility(); - } - }; - - private final ConfigurationController.ConfigurationListener mConfigurationListener = - new ConfigurationController.ConfigurationListener() { - @Override - public void onUiModeChanged() { - updateColors(); - } - - @Override - public void onThemeChanged() { - updateColors(); - } - - @Override - public void onConfigChanged(Configuration newConfig) { - updateConfiguration(); - updateColors(); - } - }; - - /** - * Handles the touch if {@link #isActionable()} is true. - * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon - * area for {@link #mLongPressTimeout} ms. - * - * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}. - */ - private boolean onTouchEvent(MotionEvent event) { - if (!actionableDownEventStartedOnView(event)) { - cancelTouches(); - return false; - } - - switch(event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_HOVER_ENTER: - if (!mDownDetected && mAccessibilityManager.isTouchExplorationEnabled()) { - vibrateOnTouchExploration(); - } - - // The pointer that causes ACTION_DOWN is always at index 0. - // We need to persist its ID to track it during ACTION_MOVE that could include - // data for many other pointers because of multi-touch support. - mActivePointerId = event.getPointerId(0); - if (mVelocityTracker == null) { - // To simplify the lifecycle of the velocity tracker, make sure it's never null - // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP. - mVelocityTracker = VelocityTracker.obtain(); - } else { - // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new - // ACTION_DOWN, in that case we should just reuse the old instance. - mVelocityTracker.clear(); - } - mVelocityTracker.addMovement(event); - - mDownDetected = true; - mLongPressCancelRunnable = mExecutor.executeDelayed( - this::onLongPress, mLongPressTimeout); - break; - case MotionEvent.ACTION_MOVE: - case MotionEvent.ACTION_HOVER_MOVE: - mVelocityTracker.addMovement(event); - // Compute pointer velocity in pixels per second. - mVelocityTracker.computeCurrentVelocity(1000); - float velocity = computePointerSpeed(mVelocityTracker, - mActivePointerId); - if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS - && exceedsVelocityThreshold(velocity)) { - Log.v(TAG, "lock icon long-press rescheduled due to " - + "high pointer velocity=" + velocity); - mLongPressCancelRunnable.run(); - mLongPressCancelRunnable = mExecutor.executeDelayed( - this::onLongPress, mLongPressTimeout); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_HOVER_EXIT: - cancelTouches(); - break; - } - - return true; - } - - /** - * Calculate the pointer speed given a velocity tracker and the pointer id. - * This assumes that the velocity tracker has already been passed all relevant motion events. - */ - private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) { - final float vx = tracker.getXVelocity(pointerId); - final float vy = tracker.getYVelocity(pointerId); - return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0)); - } - - /** - * Whether the velocity exceeds the acceptable UDFPS debouncing threshold. - */ - private static boolean exceedsVelocityThreshold(float velocity) { - return velocity > 750f; - } - - private boolean actionableDownEventStartedOnView(MotionEvent event) { - if (!isActionable()) { - return false; - } - - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - return true; - } - - return mDownDetected; - } - - @ExperimentalCoroutinesApi - @VisibleForTesting - protected void onLongPress() { - cancelTouches(); - if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) { - Log.v(TAG, "lock icon long-press rejected by the falsing manager."); - return; - } - - // pre-emptively set to true to hide view - mIsBouncerShowing = true; - if (!DeviceEntryUdfpsRefactor.isEnabled() - && mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) { - mAuthRippleController.showUnlockRipple(FINGERPRINT); - } - updateVisibility(); - - // play device entry haptic (consistent with UDFPS controller longpress) - vibrateOnLongPress(); - - if (SceneContainerFlag.isEnabled()) { - mDeviceEntryInteractor.get().attemptDeviceEntry(); - } else { - mKeyguardViewController.showPrimaryBouncer(/* scrim */ true); - } - } - - - private void cancelTouches() { - mDownDetected = false; - if (mLongPressCancelRunnable != null) { - mLongPressCancelRunnable.run(); - } - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - - private boolean isActionable() { - if (mIsBouncerShowing) { - Log.v(TAG, "lock icon long-press ignored, bouncer already showing."); - // a long press gestures from AOD may have already triggered the bouncer to show, - // so this touch is no longer actionable - return false; - } - return mUdfpsSupported || mShowUnlockIcon; - } - - /** - * Set the alpha of this view. - */ - @Override - public void setAlpha(float alpha) { - mView.setAlpha(alpha); - } - - private void updateUdfpsConfig() { - // must be called from the main thread since it may update the views - mExecutor.execute(() -> { - updateIsUdfpsEnrolled(); - updateConfiguration(); - }); - } - - @VisibleForTesting - void vibrateOnTouchExploration() { - mVibrator.performHapticFeedback( - mView, - HapticFeedbackConstants.CONTEXT_CLICK - ); - } - - @VisibleForTesting - void vibrateOnLongPress() { - mVibrator.performHapticFeedback(mView, UdfpsController.LONG_PRESS); - } - - private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { - @Override - public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { - if (modality == TYPE_FINGERPRINT) { - updateUdfpsConfig(); - } - } - - @Override - public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { - if (modality == TYPE_FINGERPRINT) { - updateUdfpsConfig(); - } - } - - @Override - public void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) { - updateUdfpsConfig(); - } - }; - - /** - * Whether the lock icon will handle a touch while dozing. - */ - @Override - public boolean willHandleTouchWhileDozing(MotionEvent event) { - // is in lock icon area - mView.getHitRect(mSensorTouchLocation); - final boolean inLockIconArea = - mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) - && mView.getVisibility() == View.VISIBLE; - - return inLockIconArea && actionableDownEventStartedOnView(event); - } - - private final View.OnClickListener mA11yClickListener = v -> onLongPress(); - - private final AccessibilityManager.AccessibilityStateChangeListener - mAccessibilityStateChangeListener = enabled -> updateAccessibility(); -} diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java deleted file mode 100644 index ff6a3d0cc6f0..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (C) 2020 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.keyguard; - -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; - -import androidx.annotation.IntDef; -import androidx.annotation.NonNull; - -import com.android.internal.graphics.ColorUtils; -import com.android.settingslib.Utils; -import com.android.systemui.Dumpable; -import com.android.systemui.res.R; - -import java.io.PrintWriter; - -/** - * A view positioned under the notification shade. - */ -public class LockIconView extends FrameLayout implements Dumpable { - @IntDef({ICON_NONE, ICON_LOCK, ICON_FINGERPRINT, ICON_UNLOCK}) - public @interface IconType {} - - public static final int ICON_NONE = -1; - public static final int ICON_LOCK = 0; - public static final int ICON_FINGERPRINT = 1; - public static final int ICON_UNLOCK = 2; - - private @IconType int mIconType; - private boolean mAod; - - @NonNull private final RectF mSensorRect; - @NonNull private Point mLockIconCenter = new Point(0, 0); - private float mRadius; - private int mLockIconPadding; - - private ImageView mLockIcon; - private ImageView mBgView; - - private int mLockIconColor; - private boolean mUseBackground = false; - private float mDozeAmount = 0f; - - @SuppressLint("ClickableViewAccessibility") - public LockIconView(Context context, AttributeSet attrs) { - super(context, attrs); - mSensorRect = new RectF(); - - addBgImageView(context, attrs); - addLockIconImageView(context, attrs); - } - - void setDozeAmount(float dozeAmount) { - mDozeAmount = dozeAmount; - updateColorAndBackgroundVisibility(); - } - - void updateColorAndBackgroundVisibility() { - if (mUseBackground && mLockIcon.getDrawable() != null) { - mLockIconColor = ColorUtils.blendARGB( - Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary), - Color.WHITE, - mDozeAmount); - int backgroundColor = Utils.getColorAttrDefaultColor(getContext(), - com.android.internal.R.attr.colorSurface); - mBgView.setImageTintList(ColorStateList.valueOf(backgroundColor)); - mBgView.setAlpha(1f - mDozeAmount); - mBgView.setVisibility(View.VISIBLE); - } else { - mLockIconColor = ColorUtils.blendARGB( - Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent), - Color.WHITE, - mDozeAmount); - mBgView.setVisibility(View.GONE); - } - - mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor)); - } - - /** - * Whether or not to render the lock icon background. Mainly used for UDPFS. - */ - public void setUseBackground(boolean useBackground) { - mUseBackground = useBackground; - updateColorAndBackgroundVisibility(); - } - - /** - * Set the location of the lock icon. - */ - public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) { - mLockIconCenter = center; - mRadius = radius; - mLockIconPadding = drawablePadding; - - mLockIcon.setPadding(mLockIconPadding, mLockIconPadding, mLockIconPadding, - mLockIconPadding); - - mSensorRect.set(mLockIconCenter.x - mRadius, - mLockIconCenter.y - mRadius, - mLockIconCenter.x + mRadius, - mLockIconCenter.y + mRadius); - - final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); - if (lp != null) { - lp.width = (int) (mSensorRect.right - mSensorRect.left); - lp.height = (int) (mSensorRect.bottom - mSensorRect.top); - lp.topMargin = (int) mSensorRect.top; - lp.setMarginStart((int) mSensorRect.left); - setLayoutParams(lp); - } - } - - @Override - public boolean hasOverlappingRendering() { - return false; - } - - float getLocationTop() { - Rect r = new Rect(); - mLockIcon.getGlobalVisibleRect(r); - return r.top; - } - - float getLocationBottom() { - Rect r = new Rect(); - mLockIcon.getGlobalVisibleRect(r); - return r.bottom; - - } - - /** - * Updates the icon its default state where no visual is shown. - */ - public void clearIcon() { - updateIcon(ICON_NONE, false); - } - - /** - * Transition the current icon to a new state - * @param icon type (ie: lock icon, unlock icon, fingerprint icon) - * @param aod whether to use the aod icon variant (some icons don't have aod variants and will - * therefore show no icon) - */ - public void updateIcon(@IconType int icon, boolean aod) { - mIconType = icon; - mAod = aod; - - mLockIcon.setImageState(getLockIconState(mIconType, mAod), true); - } - - public ImageView getLockIcon() { - return mLockIcon; - } - - private void addLockIconImageView(Context context, AttributeSet attrs) { - mLockIcon = new ImageView(context, attrs); - mLockIcon.setId(R.id.lock_icon); - mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP); - mLockIcon.setImageDrawable(context.getDrawable(R.drawable.super_lock_icon)); - addView(mLockIcon); - LayoutParams lp = (LayoutParams) mLockIcon.getLayoutParams(); - lp.height = MATCH_PARENT; - lp.width = MATCH_PARENT; - lp.gravity = Gravity.CENTER; - mLockIcon.setLayoutParams(lp); - } - - private void addBgImageView(Context context, AttributeSet attrs) { - mBgView = new ImageView(context, attrs); - mBgView.setId(R.id.lock_icon_bg); - mBgView.setImageDrawable(context.getDrawable(R.drawable.fingerprint_bg)); - mBgView.setVisibility(View.INVISIBLE); - addView(mBgView); - LayoutParams lp = (LayoutParams) mBgView.getLayoutParams(); - lp.height = MATCH_PARENT; - lp.width = MATCH_PARENT; - mBgView.setLayoutParams(lp); - } - - private static int[] getLockIconState(@IconType int icon, boolean aod) { - if (icon == ICON_NONE) { - return new int[0]; - } - - int[] lockIconState = new int[2]; - switch (icon) { - case ICON_LOCK: - lockIconState[0] = android.R.attr.state_first; - break; - case ICON_FINGERPRINT: - lockIconState[0] = android.R.attr.state_middle; - break; - case ICON_UNLOCK: - lockIconState[0] = android.R.attr.state_last; - break; - } - - if (aod) { - lockIconState[1] = android.R.attr.state_single; - } else { - lockIconState[1] = -android.R.attr.state_single; - } - - return lockIconState; - } - - private String typeToString(@IconType int type) { - switch (type) { - case ICON_NONE: - return "none"; - case ICON_LOCK: - return "lock"; - case ICON_FINGERPRINT: - return "fingerprint"; - case ICON_UNLOCK: - return "unlock"; - } - - return "invalid"; - } - - @Override - public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - pw.println("Lock Icon View Parameters:"); - pw.println(" Center in px (x, y)= (" - + mLockIconCenter.x + ", " + mLockIconCenter.y + ")"); - pw.println(" Radius in pixels: " + mRadius); - pw.println(" Drawable padding: " + mLockIconPadding); - pw.println(" mIconType=" + typeToString(mIconType)); - pw.println(" mAod=" + mAod); - pw.println("Lock Icon View actual measurements:"); - pw.println(" topLeft= (" + getX() + ", " + getY() + ")"); - pw.println(" width=" + getWidth() + " height=" + getHeight()); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt index 10d5a0cc3dd5..c5012b01dd3e 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt @@ -17,13 +17,19 @@ package com.android.keyguard import android.view.MotionEvent +import android.view.View /** Controls the [LockIconView]. */ interface LockIconViewController { - fun setLockIconView(lockIconView: LockIconView) + fun setLockIconView(lockIconView: View) + fun getTop(): Float + fun getBottom(): Float + fun dozeTimeTick() + fun setAlpha(alpha: Float) + fun willHandleTouchWhileDozing(event: MotionEvent): Boolean } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java index 158623fa80af..1978bb89b5b2 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java @@ -42,6 +42,7 @@ import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.Space; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; @@ -52,6 +53,7 @@ import androidx.annotation.VisibleForTesting; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.android.settingslib.Utils; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.HapClientProfile; @@ -428,10 +430,16 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, } catch (Resources.NotFoundException e) { Log.i(TAG, "No hearing devices related tool config resource"); } - final int listSize = toolItemList.size(); - for (int i = 0; i < listSize; i++) { + for (int i = 0; i < toolItemList.size(); i++) { View view = createHearingToolView(context, toolItemList.get(i)); mRelatedToolsContainer.addView(view); + if (i != toolItemList.size() - 1) { + final int spaceSize = context.getResources().getDimensionPixelSize( + R.dimen.hearing_devices_layout_margin); + Space space = new Space(context); + space.setLayoutParams(new LinearLayout.LayoutParams(spaceSize, 0)); + mRelatedToolsContainer.addView(space); + } } } @@ -492,6 +500,10 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, TextView text = view.requireViewById(R.id.tool_name); view.setContentDescription(item.getToolName()); icon.setImageDrawable(item.getToolIcon()); + if (item.isCustomIcon()) { + icon.getDrawable().mutate().setTint(Utils.getColorAttr(context, + com.android.internal.R.attr.materialColorOnPrimaryContainer).getDefaultColor()); + } text.setText(item.getToolName()); Intent intent = item.getToolIntent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); @@ -517,7 +529,8 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, return new ToolItem( context.getString(R.string.quick_settings_hearing_devices_live_caption_title), context.getDrawable(R.drawable.ic_volume_odi_captions), - LIVE_CAPTION_INTENT); + LIVE_CAPTION_INTENT, + /* isCustomIcon= */ true); } return null; diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java index 2006726e6847..7e4c1e5e7b6e 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java @@ -41,7 +41,7 @@ public class HearingDevicesToolItemParser { private static final String SPLIT_DELIMITER = "/"; private static final String RES_TYPE = "drawable"; @VisibleForTesting - static final int MAX_NUM = 3; + static final int MAX_NUM = 2; /** * Parses the string arrays to create a list of {@link ToolItem}. @@ -82,7 +82,8 @@ public class HearingDevicesToolItemParser { useCustomIcons ? iconList.get(i) : activityInfoList.get(i).loadIcon(packageManager), new Intent(Intent.ACTION_MAIN).setComponent( - activityInfoList.get(i).getComponentName()) + activityInfoList.get(i).getComponentName()), + useCustomIcons )); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt index 66bb2b5e2328..ef03f0cdef79 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt @@ -23,4 +23,5 @@ data class ToolItem( var toolName: String = "", var toolIcon: Drawable, var toolIntent: Intent, + var isCustomIcon: Boolean, ) diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt index cd9efaf6e6bb..610e3f8a8c84 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt @@ -39,6 +39,10 @@ import com.android.systemui.qs.tiles.impl.fontscaling.domain.FontScalingTileMapp import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileDataInteractor import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileUserActionInteractor import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.HearingDevicesTileMapper +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileDataInteractor +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor @@ -159,6 +163,13 @@ interface QSAccessibilityModule { impl: NightDisplayTileDataInteractor ): QSTileAvailabilityInteractor + @Binds + @IntoMap + @StringKey(HEARING_DEVICES_TILE_SPEC) + fun provideHearingDevicesAvailabilityInteractor( + impl: HearingDevicesTileDataInteractor + ): QSTileAvailabilityInteractor + companion object { const val COLOR_CORRECTION_TILE_SPEC = "color_correction" const val COLOR_INVERSION_TILE_SPEC = "inversion" @@ -191,7 +202,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<ColorCorrectionTileModel>, mapper: ColorCorrectionTileMapper, stateInteractor: ColorCorrectionTileDataInteractor, - userActionInteractor: ColorCorrectionUserActionInteractor + userActionInteractor: ColorCorrectionUserActionInteractor, ): QSTileViewModel = factory.create( TileSpec.create(COLOR_CORRECTION_TILE_SPEC), @@ -223,7 +234,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<ColorInversionTileModel>, mapper: ColorInversionTileMapper, stateInteractor: ColorInversionTileDataInteractor, - userActionInteractor: ColorInversionUserActionInteractor + userActionInteractor: ColorInversionUserActionInteractor, ): QSTileViewModel = factory.create( TileSpec.create(COLOR_INVERSION_TILE_SPEC), @@ -255,7 +266,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<FontScalingTileModel>, mapper: FontScalingTileMapper, stateInteractor: FontScalingTileDataInteractor, - userActionInteractor: FontScalingTileUserActionInteractor + userActionInteractor: FontScalingTileUserActionInteractor, ): QSTileViewModel = factory.create( TileSpec.create(FONT_SCALING_TILE_SPEC), @@ -279,21 +290,6 @@ interface QSAccessibilityModule { category = TileCategory.DISPLAY, ) - @Provides - @IntoMap - @StringKey(HEARING_DEVICES_TILE_SPEC) - fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = - QSTileConfig( - tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC), - uiConfig = - QSTileUIConfig.Resource( - iconRes = R.drawable.qs_hearing_devices_icon, - labelRes = R.string.quick_settings_hearing_devices_label, - ), - instanceId = uiEventLogger.getNewInstanceId(), - category = TileCategory.ACCESSIBILITY, - ) - /** * Inject Reduce Bright Colors Tile into tileViewModelMap in QSModule. The tile is hidden * behind a flag. @@ -305,7 +301,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<ReduceBrightColorsTileModel>, mapper: ReduceBrightColorsTileMapper, stateInteractor: ReduceBrightColorsTileDataInteractor, - userActionInteractor: ReduceBrightColorsTileUserActionInteractor + userActionInteractor: ReduceBrightColorsTileUserActionInteractor, ): QSTileViewModel = if (Flags.qsNewTilesFuture()) factory.create( @@ -339,7 +335,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<OneHandedModeTileModel>, mapper: OneHandedModeTileMapper, stateInteractor: OneHandedModeTileDataInteractor, - userActionInteractor: OneHandedModeTileUserActionInteractor + userActionInteractor: OneHandedModeTileUserActionInteractor, ): QSTileViewModel = if (Flags.qsNewTilesFuture()) factory.create( @@ -376,7 +372,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<NightDisplayTileModel>, mapper: NightDisplayTileMapper, stateInteractor: NightDisplayTileDataInteractor, - userActionInteractor: NightDisplayTileUserActionInteractor + userActionInteractor: NightDisplayTileUserActionInteractor, ): QSTileViewModel = if (Flags.qsNewTilesFuture()) factory.create( @@ -386,5 +382,43 @@ interface QSAccessibilityModule { mapper, ) else StubQSTileViewModel + + @Provides + @IntoMap + @StringKey(HEARING_DEVICES_TILE_SPEC) + fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = + QSTileConfig( + tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC), + uiConfig = + QSTileUIConfig.Resource( + iconRes = R.drawable.qs_hearing_devices_icon, + labelRes = R.string.quick_settings_hearing_devices_label, + ), + instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.ACCESSIBILITY, + ) + + /** + * Inject HearingDevices Tile into tileViewModelMap in QSModule. The tile is hidden behind a + * flag. + */ + @Provides + @IntoMap + @StringKey(HEARING_DEVICES_TILE_SPEC) + fun provideHearingDevicesTileViewModel( + factory: QSTileViewModelFactory.Static<HearingDevicesTileModel>, + mapper: HearingDevicesTileMapper, + stateInteractor: HearingDevicesTileDataInteractor, + userActionInteractor: HearingDevicesTileUserActionInteractor, + ): QSTileViewModel { + return if (Flags.hearingAidsQsTileDialog() && Flags.qsNewTilesFuture()) { + factory.create( + TileSpec.create(HEARING_DEVICES_TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) + } else StubQSTileViewModel + } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index c95a94e5e388..f6cc72431db0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -37,11 +37,9 @@ import com.android.systemui.biometrics.data.repository.FacePropertyRepository import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.log.core.LogLevel import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R import com.android.systemui.statusbar.CircleReveal @@ -87,7 +85,7 @@ constructor( private val lightRevealScrim: LightRevealScrim, private val authRippleInteractor: AuthRippleInteractor, private val facePropertyRepository: FacePropertyRepository, - rippleView: AuthRippleView? + rippleView: AuthRippleView?, ) : ViewController<AuthRippleView>(rippleView), CoreStartable, @@ -108,15 +106,13 @@ constructor( } init { - if (DeviceEntryUdfpsRefactor.isEnabled) { - rippleView?.repeatWhenAttached { - repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) { - authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource -> - if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) { - showUnlockRippleInternal(BiometricSourceType.FINGERPRINT) - } else { - showUnlockRippleInternal(BiometricSourceType.FACE) - } + rippleView?.repeatWhenAttached { + repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) { + authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource -> + if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) { + showUnlockRippleInternal(BiometricSourceType.FINGERPRINT) + } else { + showUnlockRippleInternal(BiometricSourceType.FACE) } } } @@ -134,29 +130,8 @@ constructor( keyguardStateController.addCallback(this) wakefulnessLifecycle.addObserver(this) commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() } - if (!DeviceEntryUdfpsRefactor.isEnabled) { - biometricUnlockController.addListener(biometricModeListener) - } } - private val biometricModeListener = - object : BiometricUnlockController.BiometricUnlockEventsListener { - override fun onBiometricUnlockedWithKeyguardDismissal( - biometricSourceType: BiometricSourceType? - ) { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - if (biometricSourceType != null) { - showUnlockRippleInternal(biometricSourceType) - } else { - logger.log( - TAG, - LogLevel.ERROR, - "Unexpected scenario where biometricSourceType is null" - ) - } - } - } - @VisibleForTesting public override fun onViewDetached() { udfpsController?.removeCallback(udfpsControllerCallback) @@ -166,17 +141,10 @@ constructor( keyguardStateController.removeCallback(this) wakefulnessLifecycle.removeObserver(this) commandRegistry.unregisterCommand("auth-ripple") - biometricUnlockController.removeListener(biometricModeListener) notificationShadeWindowController.setForcePluginOpen(false, this) } - @Deprecated("Update authRippleInteractor.showUnlockRipple instead of calling this.") - fun showUnlockRipple(biometricSourceType: BiometricSourceType) { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - showUnlockRippleInternal(biometricSourceType) - } - private fun showUnlockRippleInternal(biometricSourceType: BiometricSourceType) { val keyguardNotShowing = !keyguardStateController.isShowing val unlockNotAllowed = @@ -197,8 +165,8 @@ constructor( 0, Math.max( Math.max(it.x, displayMetrics.widthPixels - it.x), - Math.max(it.y, displayMetrics.heightPixels - it.y) - ) + Math.max(it.y, displayMetrics.heightPixels - it.y), + ), ) logger.showingUnlockRippleAt(it.x, it.y, "FP sensor radius: $udfpsRadius") showUnlockedRipple() @@ -213,8 +181,8 @@ constructor( 0, Math.max( Math.max(it.x, displayMetrics.widthPixels - it.x), - Math.max(it.y, displayMetrics.heightPixels - it.y) - ) + Math.max(it.y, displayMetrics.heightPixels - it.y), + ), ) logger.showingUnlockRippleAt(it.x, it.y, "Face unlock ripple") showUnlockedRipple() @@ -322,7 +290,7 @@ constructor( override fun onBiometricAuthenticated( userId: Int, biometricSourceType: BiometricSourceType, - isStrongBiometric: Boolean + isStrongBiometric: Boolean, ) { if (biometricSourceType == BiometricSourceType.FINGERPRINT) { mView.fadeDwellRipple() @@ -337,7 +305,7 @@ constructor( override fun onBiometricAcquired( biometricSourceType: BiometricSourceType, - acquireInfo: Int + acquireInfo: Int, ) { if ( biometricSourceType == BiometricSourceType.FINGERPRINT && diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt deleted file mode 100644 index 242601d46fa4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.biometrics - -import android.content.Context -import android.util.AttributeSet - -/** - * Class that coordinates non-HBM animations during BiometricPrompt. - * - * Currently doesn't draw anything. - * - * Note that [AuthBiometricFingerprintViewController] also shows UDFPS animations. At some point we should - * de-dupe this if necessary. - */ -class UdfpsBpView(context: Context, attrs: AttributeSet?) : UdfpsAnimationView(context, attrs) { - - // Drawable isn't ever added to the view, so we don't currently show anything - private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context) - - override fun getDrawable(): UdfpsDrawable = fingerprintDrawable -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt deleted file mode 100644 index e0455b58b919..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.biometrics - -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.phone.SystemUIDialogManager - -/** - * Class that coordinates non-HBM animations for biometric prompt. - */ -class UdfpsBpViewController( - view: UdfpsBpView, - statusBarStateController: StatusBarStateController, - shadeInteractor: ShadeInteractor, - systemUIDialogManager: SystemUIDialogManager, - dumpManager: DumpManager, - udfpsOverlayInteractor: UdfpsOverlayInteractor, -) : UdfpsAnimationViewController<UdfpsBpView>( - view, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, -) { - override val tag = "UdfpsBpViewController" - - override fun shouldPauseAuth(): Boolean { - return false - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index a3904caa9dcc..2863e29c9a34 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -87,7 +87,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.ScreenLifecycle; @@ -98,7 +97,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.shared.system.SysUiStatsLog; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.SystemUIDialogManager; @@ -162,7 +160,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull private final FalsingManager mFalsingManager; @NonNull private final PowerManager mPowerManager; @NonNull private final AccessibilityManager mAccessibilityManager; - @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; @NonNull private final ConfigurationController mConfigurationController; @NonNull private final SystemClock mSystemClock; @NonNull private final UnlockedScreenOffAnimationController @@ -283,7 +280,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mKeyguardUpdateMonitor, mDialogManager, mDumpManager, - mLockscreenShadeTransitionController, mConfigurationController, mKeyguardStateController, mUnlockedScreenOffAnimationController, @@ -291,10 +287,9 @@ public class UdfpsController implements DozeReceiver, Dumpable { requestId, reason, callback, - (view, event, fromUdfpsView) -> onTouch( + (view, event) -> onTouch( requestId, - event, - fromUdfpsView + event ), mActivityTransitionAnimator, mPrimaryBouncerInteractor, @@ -374,9 +369,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (mOverlay == null || mOverlay.isHiding()) { return; } - if (!DeviceEntryUdfpsRefactor.isEnabled()) { - ((UdfpsView) mOverlay.getTouchOverlay()).setDebugMessage(message); - } }); } @@ -391,7 +383,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { */ public void debugOnTouch(MotionEvent event) { final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L; - UdfpsController.this.onTouch(requestId, event, true); + UdfpsController.this.onTouch(requestId, event); } /** @@ -449,22 +441,10 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!mOverlayParams.equals(overlayParams)) { mOverlayParams = overlayParams; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) { - mOverlay.updateOverlayParams(mOverlayParams); - } else { - redrawOverlay(); - } + if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) { + mOverlay.updateOverlayParams(mOverlayParams); } else { - final boolean wasShowingAlternateBouncer = - mAlternateBouncerInteractor.isVisibleState(); - // When the bounds change it's always to re-create the overlay's window with new - // LayoutParams. If the overlay needs to be shown, this will re-create and show the - // overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden. redrawOverlay(); - if (wasShowingAlternateBouncer) { - mKeyguardViewManager.showBouncer(true); - } } } } @@ -563,11 +543,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } } - private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { - if (!fromUdfpsView) { - Log.e(TAG, "ignoring the touch injected from outside of UdfpsView"); - return false; - } + private boolean onTouch(long requestId, @NonNull MotionEvent event) { if (mOverlay == null) { Log.w(TAG, "ignoring onTouch with null overlay"); return false; @@ -591,13 +567,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!mIsAodInterruptActive) { mOnFingerDown = false; } - } else if (!DeviceEntryUdfpsRefactor.isEnabled()) { - if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f - && !mAlternateBouncerInteractor.isVisibleState()) - || mPrimaryBouncerInteractor.isInTransit()) { - Log.w(TAG, "ignoring touch due to qsDragProcess or primaryBouncerInteractor"); - return false; - } } final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId, @@ -661,22 +630,13 @@ public class UdfpsController implements DozeReceiver, Dumpable { mStatusBarStateController.isDozing()); break; - case UNCHANGED: - if (mActivePointerId == MotionEvent.INVALID_POINTER_ID - && mAlternateBouncerInteractor.isVisibleState()) { - // No pointer on sensor, forward to keyguard if alternateBouncer is visible - mKeyguardViewManager.onTouch(event); - } - default: break; } logBiometricTouch(processedTouch.getEvent(), data); // Always pilfer pointers that are within sensor area or when alternate bouncer is showing - if (mActivePointerId != MotionEvent.INVALID_POINTER_ID - || (mAlternateBouncerInteractor.isVisibleState() - && !DeviceEntryUdfpsRefactor.isEnabled())) { + if (mActivePointerId != MotionEvent.INVALID_POINTER_ID) { shouldPilfer = true; } @@ -692,14 +652,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } private boolean shouldTryToDismissKeyguard() { - boolean onKeyguard = false; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - onKeyguard = mKeyguardStateController.isShowing(); - } else { - onKeyguard = mOverlay != null - && mOverlay.getAnimationViewController() - instanceof UdfpsKeyguardViewControllerLegacy; - } + boolean onKeyguard = mKeyguardStateController.isShowing(); return onKeyguard && mKeyguardStateController.canDismissLockScreen() && !mAttemptedToDismissKeyguard; @@ -719,7 +672,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull FalsingManager falsingManager, @NonNull PowerManager powerManager, @NonNull AccessibilityManager accessibilityManager, - @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController, @NonNull ScreenLifecycle screenLifecycle, @NonNull VibratorHelper vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, @@ -769,7 +721,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mFalsingManager = falsingManager; mPowerManager = powerManager; mAccessibilityManager = accessibilityManager; - mLockscreenShadeTransitionController = lockscreenShadeTransitionController; screenLifecycle.addObserver(mScreenObserver); mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; mConfigurationController = configurationController; @@ -849,13 +800,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @Override public void dozeTimeTick() { - if (mOverlay != null && mOverlay.getTouchOverlay() instanceof UdfpsView) { - DeviceEntryUdfpsRefactor.assertInLegacyMode(); - final View view = mOverlay.getTouchOverlay(); - if (view != null) { - ((UdfpsView) view).dozeTimeTick(); - } - } + } private void redrawOverlay() { @@ -915,17 +860,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!isOptical()) { return; } - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if (mUdfpsDisplayMode != null) { - mUdfpsDisplayMode.disable(null); - } - } else { - if (view != null) { - UdfpsView udfpsView = (UdfpsView) view; - if (udfpsView.isDisplayConfigured()) { - udfpsView.unconfigureDisplay(); - } - } + if (mUdfpsDisplayMode != null) { + mUdfpsDisplayMode.disable(null); } } @@ -1118,11 +1054,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (mIgnoreRefreshRate) { dispatchOnUiReady(requestId); } else { - if (DeviceEntryUdfpsRefactor.isEnabled()) { mUdfpsDisplayMode.enable(() -> dispatchOnUiReady(requestId)); - } else { - ((UdfpsView) view).configureDisplay(() -> dispatchOnUiReady(requestId)); - } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 1bac0bc26a94..a1efc196dbee 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -23,8 +23,6 @@ import android.graphics.PixelFormat import android.graphics.Rect import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR import android.hardware.biometrics.BiometricRequestConstants.RequestReason @@ -42,7 +40,6 @@ import android.view.View import android.view.WindowManager import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener -import androidx.annotation.LayoutRes import androidx.annotation.VisibleForTesting import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.keyguard.KeyguardUpdateMonitor @@ -56,7 +53,6 @@ import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlay import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState @@ -64,7 +60,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController @@ -102,7 +97,6 @@ constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val dialogManager: SystemUIDialogManager, private val dumpManager: DumpManager, - private val transitionController: LockscreenShadeTransitionController, private val configurationController: ConfigurationController, private val keyguardStateController: KeyguardStateController, private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, @@ -110,7 +104,7 @@ constructor( val requestId: Long, @RequestReason val requestReason: Int, private val controllerCallback: IUdfpsOverlayControllerCallback, - private val onTouch: (View, MotionEvent, Boolean) -> Boolean, + private val onTouch: (View, MotionEvent) -> Boolean, private val activityTransitionAnimator: ActivityTransitionAnimator, private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val alternateBouncerInteractor: AlternateBouncerInteractor, @@ -133,23 +127,15 @@ constructor( .map {} // map to Unit private var listenForCurrentKeyguardState: Job? = null private var addViewRunnable: Runnable? = null - private var overlayViewLegacy: UdfpsView? = null - private set - private var overlayTouchView: UdfpsTouchOverlay? = null /** - * Get the current UDFPS overlay touch view which is a different View depending on whether the - * DeviceEntryUdfpsRefactor flag is enabled or not. + * Get the current UDFPS overlay touch view * * @return The view, when [isShowing], else null */ fun getTouchOverlay(): View? { - return if (DeviceEntryUdfpsRefactor.isEnabled) { - overlayTouchView - } else { - overlayViewLegacy - } + return overlayTouchView } private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams() @@ -161,7 +147,7 @@ constructor( WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 0 /* flags set in computeLayoutParams() */, - PixelFormat.TRANSLUCENT + PixelFormat.TRANSLUCENT, ) .apply { title = TAG @@ -188,10 +174,6 @@ constructor( val isHiding: Boolean get() = getTouchOverlay() == null - /** The animation controller if the overlay [isShowing]. */ - val animationViewController: UdfpsAnimationViewController<*>? - get() = overlayViewLegacy?.animationViewController - private var touchExplorationEnabled = false private fun shouldRemoveEnrollmentUi(): Boolean { @@ -199,7 +181,7 @@ constructor( return Settings.Global.getInt( context.contentResolver, SETTING_REMOVE_ENROLLMENT_UI, - 0 /* def */ + 0, /* def */ ) != 0 } return false @@ -212,63 +194,43 @@ constructor( overlayParams = params sensorBounds = Rect(params.sensorBounds) try { - if (DeviceEntryUdfpsRefactor.isEnabled) { - overlayTouchView = - (inflater.inflate(R.layout.udfps_touch_overlay, null, false) - as UdfpsTouchOverlay) - .apply { - // This view overlaps the sensor area - // prevent it from being selectable during a11y - if (requestReason.isImportantForAccessibility()) { - importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO - } - - addViewNowOrLater(this, null) - when (requestReason) { - REASON_AUTH_KEYGUARD -> - UdfpsTouchOverlayBinder.bind( - view = this, - viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(), - udfpsOverlayInteractor = udfpsOverlayInteractor, - ) - else -> - UdfpsTouchOverlayBinder.bind( - view = this, - viewModel = defaultUdfpsTouchOverlayViewModel.get(), - udfpsOverlayInteractor = udfpsOverlayInteractor, - ) - } - } - } else { - overlayViewLegacy = - (inflater.inflate(R.layout.udfps_view, null, false) as UdfpsView).apply { - overlayParams = params - setUdfpsDisplayModeProvider(udfpsDisplayModeProvider) - val animation = inflateUdfpsAnimation(this, controller) - if (animation != null) { - animation.init() - animationViewController = animation - } + overlayTouchView = + (inflater.inflate(R.layout.udfps_touch_overlay, null, false) + as UdfpsTouchOverlay) + .apply { // This view overlaps the sensor area // prevent it from being selectable during a11y if (requestReason.isImportantForAccessibility()) { importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO } - addViewNowOrLater(this, animation) - sensorRect = sensorBounds + addViewNowOrLater(this, null) + when (requestReason) { + REASON_AUTH_KEYGUARD -> + UdfpsTouchOverlayBinder.bind( + view = this, + viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(), + udfpsOverlayInteractor = udfpsOverlayInteractor, + ) + else -> + UdfpsTouchOverlayBinder.bind( + view = this, + viewModel = defaultUdfpsTouchOverlayViewModel.get(), + udfpsOverlayInteractor = udfpsOverlayInteractor, + ) + } } - } + getTouchOverlay()?.apply { touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled overlayTouchListener = TouchExplorationStateChangeListener { if (accessibilityManager.isTouchExplorationEnabled) { - setOnHoverListener { v, event -> onTouch(v, event, true) } + setOnHoverListener { v, event -> onTouch(v, event) } setOnTouchListener(null) touchExplorationEnabled = true } else { setOnHoverListener(null) - setOnTouchListener { v, event -> onTouch(v, event, true) } + setOnTouchListener { v, event -> onTouch(v, event) } touchExplorationEnabled = false } } @@ -312,7 +274,6 @@ constructor( } fun updateOverlayParams(updatedOverlayParams: UdfpsOverlayParams) { - DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode() overlayParams = updatedOverlayParams sensorBounds = updatedOverlayParams.sensorBounds getTouchOverlay()?.let { @@ -326,108 +287,11 @@ constructor( } } - fun inflateUdfpsAnimation( - view: UdfpsView, - controller: UdfpsController - ): UdfpsAnimationViewController<*>? { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - - val isEnrollment = - when (requestReason) { - REASON_ENROLL_FIND_SENSOR, - REASON_ENROLL_ENROLLING -> true - else -> false - } - - val filteredRequestReason = - if (isEnrollment && shouldRemoveEnrollmentUi()) { - REASON_AUTH_OTHER - } else { - requestReason - } - - return when (filteredRequestReason) { - REASON_ENROLL_FIND_SENSOR, - REASON_ENROLL_ENROLLING -> { - // Enroll udfps UI is handled by settings, so use empty view here - UdfpsFpmEmptyViewController( - view.addUdfpsView(R.layout.udfps_fpm_empty_view) { - updateAccessibilityViewLocation(sensorBounds) - }, - statusBarStateController, - shadeInteractor, - dialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - REASON_AUTH_KEYGUARD -> { - UdfpsKeyguardViewControllerLegacy( - view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) { - updateSensorLocation(sensorBounds) - }, - statusBarStateController, - statusBarKeyguardViewManager, - keyguardUpdateMonitor, - dumpManager, - transitionController, - configurationController, - keyguardStateController, - unlockedScreenOffAnimationController, - dialogManager, - controller, - activityTransitionAnimator, - primaryBouncerInteractor, - alternateBouncerInteractor, - udfpsKeyguardAccessibilityDelegate, - selectedUserInteractor, - transitionInteractor, - shadeInteractor, - udfpsOverlayInteractor, - ) - } - REASON_AUTH_BP -> { - // note: empty controller, currently shows no visual affordance - UdfpsBpViewController( - view.addUdfpsView(R.layout.udfps_bp_view), - statusBarStateController, - shadeInteractor, - dialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - REASON_AUTH_OTHER, - REASON_AUTH_SETTINGS -> { - UdfpsFpmEmptyViewController( - view.addUdfpsView(R.layout.udfps_fpm_empty_view), - statusBarStateController, - shadeInteractor, - dialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - else -> { - Log.e(TAG, "Animation for reason $requestReason not supported yet") - null - } - } - } - /** Hide the overlay or return false and do nothing if it is already hidden. */ fun hide(): Boolean { val wasShowing = isShowing - overlayViewLegacy?.apply { - if (isDisplayConfigured) { - unconfigureDisplay() - } - animationViewController = null - } - if (DeviceEntryUdfpsRefactor.isEnabled) { - udfpsDisplayModeProvider.disable(null) - } + udfpsDisplayModeProvider.disable(null) getTouchOverlay()?.apply { if (this.parent != null) { windowManager.removeView(this) @@ -440,7 +304,6 @@ constructor( } } - overlayViewLegacy = null overlayTouchView = null overlayTouchListener = null listenForCurrentKeyguardState?.cancel() @@ -490,7 +353,7 @@ constructor( Surface.rotationToString(rot) + " animation=$animation" + " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" + - " isOccluded=${keyguardStateController.isOccluded}" + " isOccluded=${keyguardStateController.isOccluded}", ) } else { Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot)) @@ -498,14 +361,14 @@ constructor( rotatedBounds, overlayParams.naturalDisplayWidth, overlayParams.naturalDisplayHeight, - rot + rot, ) RotationUtils.rotateBounds( sensorBounds, overlayParams.naturalDisplayWidth, overlayParams.naturalDisplayHeight, - rot + rot, ) } } @@ -519,14 +382,7 @@ constructor( } private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean { - val keyguardNotShowing = - if (DeviceEntryUdfpsRefactor.isEnabled) { - !keyguardStateController.isShowing - } else { - animation !is UdfpsKeyguardViewControllerLegacy - } - - if (keyguardNotShowing) { + if (!keyguardStateController.isShowing) { // always rotate view if we're not on the keyguard return true } @@ -534,16 +390,6 @@ constructor( // on the keyguard, make sure we don't rotate if we're going to sleep or not occluded return !(keyguardUpdateMonitor.isGoingToSleep || !keyguardStateController.isOccluded) } - - private inline fun <reified T : View> UdfpsView.addUdfpsView( - @LayoutRes id: Int, - init: T.() -> Unit = {} - ): T { - val subView = inflater.inflate(id, null) as T - addView(subView) - subView.init() - return subView - } } @RequestReason diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt deleted file mode 100644 index 0838855f4756..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.biometrics - -import android.content.Context -import android.graphics.Rect -import android.util.AttributeSet -import android.view.View -import android.view.ViewGroup -import com.android.systemui.res.R - -/** - * View corresponding with udfps_fpm_empty_view.xml - * - * Currently doesn't draw anything. - */ -class UdfpsFpmEmptyView( - context: Context, - attrs: AttributeSet? -) : UdfpsAnimationView(context, attrs) { - - // Drawable isn't ever added to the view, so we don't currently show anything - private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context) - - override fun getDrawable(): UdfpsDrawable = fingerprintDrawable - - fun updateAccessibilityViewLocation(sensorBounds: Rect) { - val fingerprintAccessibilityView: View = - requireViewById(R.id.udfps_enroll_accessibility_view) - val params: ViewGroup.LayoutParams = fingerprintAccessibilityView.layoutParams - params.width = sensorBounds.width() - params.height = sensorBounds.height() - fingerprintAccessibilityView.layoutParams = params - fingerprintAccessibilityView.requestLayout() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt deleted file mode 100644 index cfbbc26800d2..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.biometrics - -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.phone.SystemUIDialogManager - -/** - * Class that coordinates non-HBM animations for non keyguard, or biometric prompt states. - * - * Currently doesn't draw anything. - */ -class UdfpsFpmEmptyViewController( - view: UdfpsFpmEmptyView, - statusBarStateController: StatusBarStateController, - shadeInteractor: ShadeInteractor, - systemUIDialogManager: SystemUIDialogManager, - dumpManager: DumpManager, - udfpsOverlayInteractor: UdfpsOverlayInteractor, -) : UdfpsAnimationViewController<UdfpsFpmEmptyView>( - view, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, -) { - override val tag = "UdfpsFpmOtherViewController" -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt deleted file mode 100644 index c3d9240c40a1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ /dev/null @@ -1,555 +0,0 @@ -/* - * 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.biometrics - -import android.content.res.Configuration -import android.util.MathUtils -import android.view.View -import androidx.annotation.VisibleForTesting -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.repeatOnLifecycle -import com.android.app.animation.Interpolators -import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.animation.ActivityTransitionAnimator -import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.Edge -import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER -import com.android.systemui.keyguard.shared.model.KeyguardState.AOD -import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING -import com.android.systemui.keyguard.shared.model.KeyguardState.GONE -import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED -import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER -import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.res.R -import com.android.systemui.scene.shared.model.Scenes -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.LockscreenShadeTransitionController -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.OccludingAppBiometricUI -import com.android.systemui.statusbar.phone.SystemUIDialogManager -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.user.domain.interactor.SelectedUserInteractor -import java.io.PrintWriter -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch - -/** Class that coordinates non-HBM animations during keyguard authentication. */ -@ExperimentalCoroutinesApi -open class UdfpsKeyguardViewControllerLegacy( - private val view: UdfpsKeyguardViewLegacy, - statusBarStateController: StatusBarStateController, - private val keyguardViewManager: StatusBarKeyguardViewManager, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - dumpManager: DumpManager, - private val lockScreenShadeTransitionController: LockscreenShadeTransitionController, - private val configurationController: ConfigurationController, - private val keyguardStateController: KeyguardStateController, - private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, - systemUIDialogManager: SystemUIDialogManager, - private val udfpsController: UdfpsController, - private val activityTransitionAnimator: ActivityTransitionAnimator, - private val primaryBouncerInteractor: PrimaryBouncerInteractor, - private val alternateBouncerInteractor: AlternateBouncerInteractor, - private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate, - private val selectedUserInteractor: SelectedUserInteractor, - private val transitionInteractor: KeyguardTransitionInteractor, - shadeInteractor: ShadeInteractor, - udfpsOverlayInteractor: UdfpsOverlayInteractor, -) : - UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>( - view, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, - ) { - private val uniqueIdentifier = this.toString() - private var showingUdfpsBouncer = false - private var udfpsRequested = false - private var qsExpansion = 0f - private var faceDetectRunning = false - private var statusBarState = 0 - private var transitionToFullShadeProgress = 0f - private var lastDozeAmount = 0f - private var panelExpansionFraction = 0f - private var launchTransitionFadingAway = false - private var isLaunchingActivity = false - private var activityLaunchProgress = 0f - private var inputBouncerExpansion = 0f - - private val stateListener: StatusBarStateController.StateListener = - object : StatusBarStateController.StateListener { - override fun onStateChanged(statusBarState: Int) { - this@UdfpsKeyguardViewControllerLegacy.statusBarState = statusBarState - updateAlpha() - updatePauseAuth() - } - } - - private val configurationListener: ConfigurationController.ConfigurationListener = - object : ConfigurationController.ConfigurationListener { - override fun onUiModeChanged() { - view.updateColor() - } - - override fun onThemeChanged() { - view.updateColor() - } - - override fun onConfigChanged(newConfig: Configuration) { - updateScaleFactor() - view.updatePadding() - view.updateColor() - } - } - - private val keyguardStateControllerCallback: KeyguardStateController.Callback = - object : KeyguardStateController.Callback { - override fun onUnlockedChanged() { - updatePauseAuth() - } - - override fun onLaunchTransitionFadingAwayChanged() { - launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway - updatePauseAuth() - } - } - - private val mActivityTransitionAnimatorListener: ActivityTransitionAnimator.Listener = - object : ActivityTransitionAnimator.Listener { - override fun onTransitionAnimationStart() { - isLaunchingActivity = true - activityLaunchProgress = 0f - updateAlpha() - } - - override fun onTransitionAnimationEnd() { - isLaunchingActivity = false - updateAlpha() - } - - override fun onTransitionAnimationProgress(linearProgress: Float) { - activityLaunchProgress = linearProgress - updateAlpha() - } - } - - private val statusBarKeyguardViewManagerCallback: KeyguardViewManagerCallback = - object : KeyguardViewManagerCallback { - override fun onQSExpansionChanged(qsExpansion: Float) { - this@UdfpsKeyguardViewControllerLegacy.qsExpansion = qsExpansion - updateAlpha() - updatePauseAuth() - } - } - - private val occludingAppBiometricUI: OccludingAppBiometricUI = - object : OccludingAppBiometricUI { - override fun requestUdfps(request: Boolean, color: Int) { - udfpsRequested = request - view.requestUdfps(request, color) - updateAlpha() - updatePauseAuth() - } - - override fun dump(pw: PrintWriter) { - pw.println(tag) - } - } - - override val tag: String - get() = TAG - - override fun onInit() { - super.onInit() - keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI) - } - - init { - com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor.assertInLegacyMode() - view.repeatWhenAttached { - // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion - // can make the view not visible; and we still want to listen for events - // that may make the view visible again. - repeatOnLifecycle(Lifecycle.State.CREATED) { - listenForBouncerExpansion(this) - listenForAlternateBouncerVisibility(this) - listenForOccludedToAodTransition(this) - listenForGoneToAodTransition(this) - listenForLockscreenAodTransitions(this) - listenForAodToOccludedTransitions(this) - listenForAlternateBouncerToAodTransitions(this) - listenForDreamingToAodTransitions(this) - listenForPrimaryBouncerToAodTransitions(this) - } - } - } - - @VisibleForTesting - suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor - .transition( - edge = Edge.create(Scenes.Bouncer, AOD), - edgeWithoutSceneContainer = Edge.create(PRIMARY_BOUNCER, AOD) - ) - .collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transition(Edge.create(DREAMING, AOD)).collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transition(Edge.create(ALTERNATE_BOUNCER, AOD)).collect { - transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForAodToOccludedTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transition(Edge.create(AOD, OCCLUDED)).collect { transitionStep -> - view.onDozeAmountChanged( - 1f - transitionStep.value, - 1f - transitionStep.value, - UdfpsKeyguardViewLegacy.ANIMATION_NONE, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForOccludedToAodTransition(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transition(Edge.create(OCCLUDED, AOD)).collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor - .transition( - edge = Edge.create(Scenes.Gone, AOD), - edgeWithoutSceneContainer = Edge.create(GONE, AOD) - ) - .collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transitionValue(AOD).collect { - view.onDozeAmountChanged( - it, - it, - UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job { - return scope.launch { - primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float -> - inputBouncerExpansion = bouncerExpansion - - panelExpansionFraction = - if (keyguardViewManager.isPrimaryBouncerInTransit) { - aboutToShowBouncerProgress(1f - bouncerExpansion) - } else { - 1f - bouncerExpansion - } - updateAlpha() - updatePauseAuth() - } - } - } - - @VisibleForTesting - suspend fun listenForAlternateBouncerVisibility(scope: CoroutineScope): Job { - return scope.launch { - alternateBouncerInteractor.isVisible.collect { isVisible: Boolean -> - showUdfpsBouncer(isVisible) - } - } - } - - public override fun onViewAttached() { - super.onViewAttached() - alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, uniqueIdentifier) - val dozeAmount = statusBarStateController.dozeAmount - lastDozeAmount = dozeAmount - stateListener.onDozeAmountChanged(dozeAmount, dozeAmount) - statusBarStateController.addCallback(stateListener) - udfpsRequested = false - launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway - keyguardStateController.addCallback(keyguardStateControllerCallback) - statusBarState = statusBarStateController.state - qsExpansion = keyguardViewManager.qsExpansion - keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback) - configurationController.addCallback(configurationListener) - updateScaleFactor() - view.updatePadding() - updateAlpha() - updatePauseAuth() - keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI) - lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this - activityTransitionAnimator.addListener(mActivityTransitionAnimatorListener) - view.startIconAsyncInflate { - val animationViewInternal: View = - view.requireViewById(R.id.udfps_animation_view_internal) - animationViewInternal.accessibilityDelegate = udfpsKeyguardAccessibilityDelegate - } - } - - public override fun onViewDetached() { - super.onViewDetached() - alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier) - faceDetectRunning = false - keyguardStateController.removeCallback(keyguardStateControllerCallback) - statusBarStateController.removeCallback(stateListener) - keyguardViewManager.removeOccludingAppBiometricUI(occludingAppBiometricUI) - configurationController.removeCallback(configurationListener) - if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) { - lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null - } - activityTransitionAnimator.removeListener(mActivityTransitionAnimatorListener) - keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback) - } - - override fun dump(pw: PrintWriter, args: Array<String>) { - super.dump(pw, args) - pw.println("showingUdfpsAltBouncer=$showingUdfpsBouncer") - pw.println( - "altBouncerInteractor#isAlternateBouncerVisible=" + - "${alternateBouncerInteractor.isVisibleState()}" - ) - pw.println( - "altBouncerInteractor#canShowAlternateBouncerForFingerprint=" + - "${alternateBouncerInteractor.canShowAlternateBouncerForFingerprint()}" - ) - pw.println("faceDetectRunning=$faceDetectRunning") - pw.println("statusBarState=" + StatusBarState.toString(statusBarState)) - pw.println("transitionToFullShadeProgress=$transitionToFullShadeProgress") - pw.println("qsExpansion=$qsExpansion") - pw.println("panelExpansionFraction=$panelExpansionFraction") - pw.println("unpausedAlpha=" + view.unpausedAlpha) - pw.println("udfpsRequestedByApp=$udfpsRequested") - pw.println("launchTransitionFadingAway=$launchTransitionFadingAway") - pw.println("lastDozeAmount=$lastDozeAmount") - pw.println("inputBouncerExpansion=$inputBouncerExpansion") - view.dump(pw) - } - - /** - * Overrides non-bouncer show logic in shouldPauseAuth to still show icon. - * - * @return whether the udfpsBouncer has been newly shown or hidden - */ - private fun showUdfpsBouncer(show: Boolean): Boolean { - if (showingUdfpsBouncer == show) { - return false - } - val udfpsAffordanceWasNotShowing = shouldPauseAuth() - showingUdfpsBouncer = show - if (showingUdfpsBouncer) { - if (udfpsAffordanceWasNotShowing) { - view.animateInUdfpsBouncer(null) - } - view.announceForAccessibility( - view.context.getString(R.string.accessibility_fingerprint_bouncer) - ) - } - updateAlpha() - updatePauseAuth() - return true - } - - /** - * Returns true if the fingerprint manager is running but we want to temporarily pause - * authentication. On the keyguard, we may want to show udfps when the shade is expanded, so - * this can be overridden with the showBouncer method. - */ - override fun shouldPauseAuth(): Boolean { - if (showingUdfpsBouncer) { - return false - } - if ( - udfpsRequested && - !notificationShadeVisible && - !isInputBouncerFullyVisible() && - keyguardStateController.isShowing - ) { - return false - } - if (launchTransitionFadingAway) { - return true - } - - // Only pause auth if we're not on the keyguard AND we're not transitioning to doze. - // For the UnlockedScreenOffAnimation, the statusBarState is - // delayed. However, we still animate in the UDFPS affordance with the - // unlockedScreenOffDozeAnimator. - if ( - statusBarState != StatusBarState.KEYGUARD && - !unlockedScreenOffAnimationController.isAnimationPlaying() - ) { - return true - } - if (isBouncerExpansionGreaterThan(.5f)) { - return true - } - if ( - keyguardUpdateMonitor.getUserUnlockedWithBiometric( - selectedUserInteractor.getSelectedUserId() - ) - ) { - // If the device was unlocked by a biometric, immediately hide the UDFPS icon to avoid - // overlap with the LockIconView. Shortly afterwards, UDFPS will stop running. - return true - } - return view.unpausedAlpha < 255 * .1 - } - - fun isBouncerExpansionGreaterThan(bouncerExpansionThreshold: Float): Boolean { - return inputBouncerExpansion >= bouncerExpansionThreshold - } - - fun isInputBouncerFullyVisible(): Boolean { - return inputBouncerExpansion == 1f - } - - override fun listenForTouchesOutsideView(): Boolean { - return true - } - - /** - * Set the progress we're currently transitioning to the full shade. 0.0f means we're not - * transitioning yet, while 1.0f means we've fully dragged down. For example, start swiping down - * to expand the notification shade from the empty space in the middle of the lock screen. - */ - fun setTransitionToFullShadeProgress(progress: Float) { - transitionToFullShadeProgress = progress - updateAlpha() - } - - /** - * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's alpha is - * based on the doze amount. - */ - override fun updateAlpha() { - // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested, - // then the keyguard is occluded by some application - so instead use the input bouncer - // hidden amount to determine the fade. - val expansion = if (udfpsRequested) getInputBouncerHiddenAmt() else panelExpansionFraction - var alpha: Int = - if (showingUdfpsBouncer) 255 - else MathUtils.constrain(MathUtils.map(.5f, .9f, 0f, 255f, expansion), 0f, 255f).toInt() - if (!showingUdfpsBouncer) { - // swipe from top of the lockscreen to expand full QS: - alpha = - (alpha * (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(qsExpansion))) - .toInt() - - // swipe from the middle (empty space) of lockscreen to expand the notification shade: - alpha = (alpha * (1.0f - transitionToFullShadeProgress)).toInt() - - // Fade out the icon if we are animating an activity launch over the lockscreen and the - // activity didn't request the UDFPS. - if (isLaunchingActivity && !udfpsRequested) { - val udfpsActivityLaunchAlphaMultiplier = - 1f - - (activityLaunchProgress * - (ActivityTransitionAnimator.TIMINGS.totalDuration / 83)) - .coerceIn(0f, 1f) - alpha = (alpha * udfpsActivityLaunchAlphaMultiplier).toInt() - } - - // Fade out alpha when a dialog is shown - // Fade in alpha when a dialog is hidden - alpha = (alpha * view.dialogSuggestedAlpha).toInt() - } - view.unpausedAlpha = alpha - } - - private fun getInputBouncerHiddenAmt(): Float { - return 1f - inputBouncerExpansion - } - - /** Update the scale factor based on the device's resolution. */ - private fun updateScaleFactor() { - udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) } - } - - companion object { - const val TAG = "UdfpsKeyguardViewController" - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java deleted file mode 100644 index 6d4eea852d26..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * 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.biometrics; - -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.Rect; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.MathUtils; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; - -import androidx.annotation.IntDef; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.asynclayoutinflater.view.AsyncLayoutInflater; - -import com.android.app.animation.Interpolators; -import com.android.settingslib.Utils; -import com.android.systemui.res.R; - -import com.airbnb.lottie.LottieAnimationView; -import com.airbnb.lottie.LottieProperty; -import com.airbnb.lottie.model.KeyPath; - -import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * View corresponding with udfps_keyguard_view_legacy.xml - */ -public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView { - private UdfpsDrawable mFingerprintDrawable; // placeholder - private LottieAnimationView mAodFp; - private LottieAnimationView mLockScreenFp; - - // used when highlighting fp icon: - private int mTextColorPrimary; - private ImageView mBgProtection; - boolean mUdfpsRequested; - - private AnimatorSet mBackgroundInAnimator = new AnimatorSet(); - private int mAlpha; // 0-255 - private float mScaleFactor = 1; - private Rect mSensorBounds = new Rect(); - - // AOD anti-burn-in offsets - private final int mMaxBurnInOffsetX; - private final int mMaxBurnInOffsetY; - private float mInterpolatedDarkAmount; - private int mAnimationType = ANIMATION_NONE; - private boolean mFullyInflated; - private Runnable mOnFinishInflateRunnable; - - public UdfpsKeyguardViewLegacy(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - mFingerprintDrawable = new UdfpsFpDrawable(context); - - mMaxBurnInOffsetX = context.getResources() - .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); - mMaxBurnInOffsetY = context.getResources() - .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); - } - - /** - * Inflate internal udfps view on a background thread and call the onFinishRunnable - * when inflation is finished. - */ - public void startIconAsyncInflate(Runnable onFinishInflate) { - mOnFinishInflateRunnable = onFinishInflate; - // inflate Lottie views on a background thread in case it takes a while to inflate - AsyncLayoutInflater inflater = new AsyncLayoutInflater(mContext); - inflater.inflate(R.layout.udfps_keyguard_view_internal, this, - mLayoutInflaterFinishListener); - } - - @Override - public UdfpsDrawable getDrawable() { - return mFingerprintDrawable; - } - - @Override - void onSensorRectUpdated(RectF bounds) { - super.onSensorRectUpdated(bounds); - bounds.round(this.mSensorBounds); - postInvalidate(); - } - - @Override - void onDisplayConfiguring() { - } - - @Override - void onDisplayUnconfigured() { - } - - @Override - public boolean dozeTimeTick() { - updateBurnInOffsets(); - return true; - } - - private void updateBurnInOffsets() { - if (!mFullyInflated) { - return; - } - - // if we're animating from screen off, we can immediately place the icon in the - // AoD-burn in location, else we need to translate the icon from LS => AoD. - final float darkAmountForAnimation = mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF - ? 1f : mInterpolatedDarkAmount; - final float burnInOffsetX = MathUtils.lerp(0f, - getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */) - - mMaxBurnInOffsetX, darkAmountForAnimation); - final float burnInOffsetY = MathUtils.lerp(0f, - getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) - - mMaxBurnInOffsetY, darkAmountForAnimation); - final float burnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(), - darkAmountForAnimation); - - if (mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN && !mPauseAuth) { - mLockScreenFp.setTranslationX(burnInOffsetX); - mLockScreenFp.setTranslationY(burnInOffsetY); - mBgProtection.setAlpha(1f - mInterpolatedDarkAmount); - mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount); - } else if (darkAmountForAnimation == 0f) { - // we're on the lockscreen and should use mAlpha (changes based on shade expansion) - mLockScreenFp.setTranslationX(0); - mLockScreenFp.setTranslationY(0); - mBgProtection.setAlpha(mAlpha / 255f); - mLockScreenFp.setAlpha(mAlpha / 255f); - } else { - mBgProtection.setAlpha(0f); - mLockScreenFp.setAlpha(0f); - } - mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount); - - mAodFp.setTranslationX(burnInOffsetX); - mAodFp.setTranslationY(burnInOffsetY); - mAodFp.setProgress(burnInProgress); - mAodFp.setAlpha(mInterpolatedDarkAmount); - - // done animating - final boolean doneAnimatingBetweenAodAndLS = - mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN - && (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f); - final boolean doneAnimatingUnlockedScreenOff = - mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF - && (mInterpolatedDarkAmount == 1f); - if (doneAnimatingBetweenAodAndLS || doneAnimatingUnlockedScreenOff) { - mAnimationType = ANIMATION_NONE; - } - } - - void requestUdfps(boolean request, int color) { - mUdfpsRequested = request; - } - - void updateColor() { - if (!mFullyInflated) { - return; - } - - mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext, - com.android.internal.R.attr.materialColorOnSurface); - final int backgroundColor = Utils.getColorAttrDefaultColor(getContext(), - com.android.internal.R.attr.materialColorSurfaceContainerHigh); - mBgProtection.setImageTintList(ColorStateList.valueOf(backgroundColor)); - mLockScreenFp.invalidate(); // updated with a valueCallback - } - - void setScaleFactor(float scale) { - mScaleFactor = scale; - } - - void updatePadding() { - if (mLockScreenFp == null || mAodFp == null) { - return; - } - - final int defaultPaddingPx = - getResources().getDimensionPixelSize(R.dimen.lock_icon_padding); - final int padding = (int) (defaultPaddingPx * mScaleFactor); - mLockScreenFp.setPadding(padding, padding, padding, padding); - mAodFp.setPadding(padding, padding, padding, padding); - } - - /** - * @param alpha between 0 and 255 - */ - void setUnpausedAlpha(int alpha) { - mAlpha = alpha; - updateAlpha(); - } - - /** - * @return alpha between 0 and 255 - */ - int getUnpausedAlpha() { - return mAlpha; - } - - @Override - protected int updateAlpha() { - int alpha = super.updateAlpha(); - updateBurnInOffsets(); - return alpha; - } - - @Override - int calculateAlpha() { - if (mPauseAuth) { - return 0; - } - return mAlpha; - } - - static final int ANIMATION_NONE = 0; - static final int ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN = 1; - static final int ANIMATE_APPEAR_ON_SCREEN_OFF = 2; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ANIMATION_NONE, ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, ANIMATE_APPEAR_ON_SCREEN_OFF}) - private @interface AnimationType {} - - void onDozeAmountChanged(float linear, float eased, @AnimationType int animationType) { - mAnimationType = animationType; - mInterpolatedDarkAmount = eased; - updateAlpha(); - } - - void updateSensorLocation(@NonNull Rect sensorBounds) { - mSensorBounds.set(sensorBounds); - } - - /** - * Animates in the bg protection circle behind the fp icon to highlight the icon. - */ - void animateInUdfpsBouncer(Runnable onEndAnimation) { - if (mBackgroundInAnimator.isRunning() || !mFullyInflated) { - // already animating in or not yet inflated - return; - } - - // fade in and scale up - mBackgroundInAnimator = new AnimatorSet(); - mBackgroundInAnimator.playTogether( - ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f), - ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f), - ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f)); - mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mBackgroundInAnimator.setDuration(500); - mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (onEndAnimation != null) { - onEndAnimation.run(); - } - } - }); - mBackgroundInAnimator.start(); - } - - /** - * Print debugging information for this class. - */ - public void dump(PrintWriter pw) { - pw.println("UdfpsKeyguardView (" + this + ")"); - pw.println(" mPauseAuth=" + mPauseAuth); - pw.println(" mUnpausedAlpha=" + getUnpausedAlpha()); - pw.println(" mUdfpsRequested=" + mUdfpsRequested); - pw.println(" mInterpolatedDarkAmount=" + mInterpolatedDarkAmount); - pw.println(" mAnimationType=" + mAnimationType); - } - - private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener = - new AsyncLayoutInflater.OnInflateFinishedListener() { - @Override - public void onInflateFinished(View view, int resid, ViewGroup parent) { - mFullyInflated = true; - mAodFp = view.findViewById(R.id.udfps_aod_fp); - mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp); - mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg); - - updatePadding(); - updateColor(); - updateAlpha(); - - final LayoutParams lp = (LayoutParams) view.getLayoutParams(); - lp.width = mSensorBounds.width(); - lp.height = mSensorBounds.height(); - RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds)); - lp.setMarginsRelative((int) relativeToView.left, (int) relativeToView.top, - (int) relativeToView.right, (int) relativeToView.bottom); - parent.addView(view, lp); - - // requires call to invalidate to update the color - mLockScreenFp.addValueCallback(new KeyPath("**"), LottieProperty.COLOR_FILTER, - frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, - PorterDuff.Mode.SRC_ATOP)); - mOnFinishInflateRunnable.run(); - } - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt deleted file mode 100644 index 76bcd6e2863b..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2020 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.biometrics - -import android.content.Context -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.Paint -import android.graphics.Rect -import android.graphics.RectF -import android.util.AttributeSet -import android.util.Log -import android.view.MotionEvent -import android.widget.FrameLayout -import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams -import com.android.systemui.doze.DozeReceiver - -private const val TAG = "UdfpsView" - -/** - * The main view group containing all UDFPS animations. - */ -class UdfpsView( - context: Context, - attrs: AttributeSet? -) : FrameLayout(context, attrs), DozeReceiver { - // sensorRect may be bigger than the sensor. True sensor dimensions are defined in - // overlayParams.sensorBounds - var sensorRect = Rect() - private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null - private val debugTextPaint = Paint().apply { - isAntiAlias = true - color = Color.BLUE - textSize = 32f - } - - /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */ - var animationViewController: UdfpsAnimationViewController<*>? = null - - /** Parameters that affect the position and size of the overlay. */ - var overlayParams = UdfpsOverlayParams() - - /** Debug message. */ - var debugMessage: String? = null - set(value) { - field = value - postInvalidate() - } - - /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */ - var isDisplayConfigured: Boolean = false - private set - - fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) { - mUdfpsDisplayMode = udfpsDisplayModeProvider - } - - // Don't propagate any touch events to the child views. - override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { - return (animationViewController == null || !animationViewController!!.shouldPauseAuth()) - } - - override fun dozeTimeTick() { - animationViewController?.dozeTimeTick() - } - - override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { - super.onLayout(changed, left, top, right, bottom) - - // Updates sensor rect in relation to the overlay view - animationViewController?.onSensorRectUpdated(RectF(sensorRect)) - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - Log.v(TAG, "onAttachedToWindow") - } - - override fun onDetachedFromWindow() { - super.onDetachedFromWindow() - Log.v(TAG, "onDetachedFromWindow") - } - - override fun onDraw(canvas: Canvas) { - super.onDraw(canvas) - if (!isDisplayConfigured) { - if (!debugMessage.isNullOrEmpty()) { - canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint) - } - } - } - - fun configureDisplay(onDisplayConfigured: Runnable) { - isDisplayConfigured = true - animationViewController?.onDisplayConfiguring() - mUdfpsDisplayMode?.enable(onDisplayConfigured) - } - - fun unconfigureDisplay() { - isDisplayConfigured = false - animationViewController?.onDisplayUnconfigured() - mUdfpsDisplayMode?.disable(null /* onDisabled */) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt index 7503a8b4362d..a105d663424c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt @@ -23,7 +23,6 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay import com.android.systemui.biometrics.ui.viewmodel.UdfpsTouchOverlayViewModel -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.lifecycle.repeatWhenAttached import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch @@ -42,14 +41,13 @@ object UdfpsTouchOverlayBinder { viewModel: UdfpsTouchOverlayViewModel, udfpsOverlayInteractor: UdfpsOverlayInteractor, ) { - if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { launch { viewModel.shouldHandleTouches.collect { shouldHandleTouches -> Log.d( "UdfpsTouchOverlayBinder", - "[$view]: update shouldHandleTouches=$shouldHandleTouches" + "[$view]: update shouldHandleTouches=$shouldHandleTouches", ) view.isInvisible = !shouldHandleTouches udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches) @@ -58,7 +56,7 @@ object UdfpsTouchOverlayBinder { .invokeOnCompletion { Log.d( "UdfpsTouchOverlayBinder", - "[$view-detached]: update shouldHandleTouches=false" + "[$view-detached]: update shouldHandleTouches=false", ) udfpsOverlayInteractor.setHandleTouches(false) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt index f1c3f949ffba..22b2888a51a9 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt @@ -24,7 +24,6 @@ import com.android.systemui.bouncer.shared.model.BouncerDismissActionModel import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.log.dagger.BouncerTableLog import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable @@ -88,7 +87,6 @@ interface KeyguardBouncerRepository { val showMessage: StateFlow<BouncerShowMessageModel?> val resourceUpdateRequests: StateFlow<Boolean> val alternateBouncerVisible: StateFlow<Boolean> - val alternateBouncerUIAvailable: StateFlow<Boolean> /** Last shown security mode of the primary bouncer (ie: pin/pattern/password/SIM) */ val lastShownSecurityMode: StateFlow<KeyguardSecurityModel.SecurityMode> @@ -126,8 +124,6 @@ interface KeyguardBouncerRepository { fun setAlternateVisible(isVisible: Boolean) - fun setAlternateBouncerUIAvailable(isAvailable: Boolean) - fun setLastShownSecurityMode(securityMode: KeyguardSecurityModel.SecurityMode) } @@ -199,9 +195,6 @@ constructor( private val _alternateBouncerVisible = MutableStateFlow(false) override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow() override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE - private val _alternateBouncerUIAvailable = MutableStateFlow(false) - override val alternateBouncerUIAvailable: StateFlow<Boolean> = - _alternateBouncerUIAvailable.asStateFlow() init { setUpLogging() @@ -220,11 +213,6 @@ constructor( _alternateBouncerVisible.value = isVisible } - override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - _alternateBouncerUIAvailable.value = isAvailable - } - override fun setPrimaryShow(isShowing: Boolean) { _primaryBouncerShow.value = isShowing } @@ -320,9 +308,6 @@ constructor( resourceUpdateRequests .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false) .launchIn(applicationScope) - alternateBouncerUIAvailable - .logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false) - .launchIn(applicationScope) alternateBouncerVisible .logDiffsForTable(buffer, "", "AlternateBouncerVisible", false) .launchIn(applicationScope) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt index 0949ea4d7797..9c2a10a444e2 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt @@ -17,22 +17,17 @@ package com.android.systemui.bouncer.domain.interactor import android.util.Log -import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor -import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes -import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf import com.android.systemui.util.time.SystemClock import dagger.Lazy @@ -55,13 +50,9 @@ import kotlinx.coroutines.flow.stateIn class AlternateBouncerInteractor @Inject constructor( - private val statusBarStateController: StatusBarStateController, - private val keyguardStateController: KeyguardStateController, private val bouncerRepository: KeyguardBouncerRepository, fingerprintPropertyRepository: FingerprintPropertyRepository, - private val biometricSettingsRepository: BiometricSettingsRepository, private val systemClock: SystemClock, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val deviceEntryBiometricsAllowedInteractor: Lazy<DeviceEntryBiometricsAllowedInteractor>, private val keyguardInteractor: Lazy<KeyguardInteractor>, @@ -73,17 +64,10 @@ constructor( val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet() val alternateBouncerSupported: StateFlow<Boolean> = - if (DeviceEntryUdfpsRefactor.isEnabled) { - fingerprintPropertyRepository.sensorType - .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() } - .stateIn( - scope = scope, - started = SharingStarted.Eagerly, - initialValue = false, - ) - } else { - bouncerRepository.alternateBouncerUIAvailable - } + fingerprintPropertyRepository.sensorType + .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() } + .stateIn(scope = scope, started = SharingStarted.Eagerly, initialValue = false) + private val isDozingOrAod: Flow<Boolean> = anyOf( keyguardTransitionInteractor.get().transitionValue(KeyguardState.DOZING).map { @@ -106,7 +90,7 @@ constructor( combine( keyguardTransitionInteractor.get().currentKeyguardState, sceneInteractor.get().currentScene, - ::Pair + ::Pair, ) .flatMapLatest { (currentKeyguardState, transitionState) -> if (currentKeyguardState == KeyguardState.GONE) { @@ -122,7 +106,7 @@ constructor( .isFingerprintAuthCurrentlyAllowed, keyguardInteractor.get().isKeyguardDismissible, bouncerRepository.primaryBouncerShow, - isDozingOrAod + isDozingOrAod, ) { fingerprintAllowed, keyguardDismissible, @@ -141,37 +125,17 @@ constructor( } .distinctUntilChanged() .onEach { Log.d(TAG, "canShowAlternateBouncer changed to $it") } - .stateIn( - scope = scope, - started = WhileSubscribed(), - initialValue = false, - ) + .stateIn(scope = scope, started = WhileSubscribed(), initialValue = false) /** * Always shows the alternate bouncer. Requesters must check [canShowAlternateBouncer]` before * calling this. */ fun forceShow() { - if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) { - show() - return - } bouncerRepository.setAlternateVisible(true) } /** - * Sets the correct bouncer states to show the alternate bouncer if it can show. - * - * @return whether alternateBouncer is visible - * @deprecated use [forceShow] and manually check [canShowAlternateBouncer] beforehand - */ - fun show(): Boolean { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - bouncerRepository.setAlternateVisible(canShowAlternateBouncerForFingerprint()) - return isVisibleState() - } - - /** * Sets the correct bouncer states to hide the bouncer. Should only be called through * StatusBarKeyguardViewManager until ScrimController is refactored to use * alternateBouncerInteractor. @@ -189,28 +153,8 @@ constructor( return bouncerRepository.alternateBouncerVisible.value } - fun setAlternateBouncerUIAvailable(isAvailable: Boolean, token: String) { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - if (isAvailable) { - alternateBouncerUiAvailableFromSource.add(token) - } else { - alternateBouncerUiAvailableFromSource.remove(token) - } - bouncerRepository.setAlternateBouncerUIAvailable( - alternateBouncerUiAvailableFromSource.isNotEmpty() - ) - } - fun canShowAlternateBouncerForFingerprint(): Boolean { - if (DeviceEntryUdfpsRefactor.isEnabled) { - return canShowAlternateBouncer.value - } - return alternateBouncerSupported.value && - biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value && - !keyguardUpdateMonitor.isFingerprintLockedOut && - !keyguardStateController.isUnlocked && - !statusBarStateController.isDozing && - !bouncerRepository.primaryBouncerShow.value + return canShowAlternateBouncer.value } /** diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt index 2c026c0bb5ce..da01c583db0a 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt @@ -30,6 +30,7 @@ import com.android.internal.util.EmergencyAffordanceManager import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.bouncer.data.repository.EmergencyServicesRepository import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel +import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background @@ -74,6 +75,7 @@ constructor( private val metricsLogger: MetricsLogger, private val dozeLogger: DozeLogger, private val sceneInteractor: Lazy<SceneInteractor>, + private val bouncerHapticPlayer: BouncerHapticPlayer, ) { /** The bouncer action button. If `null`, the button should not be shown. */ val actionButton: Flow<BouncerActionButtonModel?> = @@ -111,6 +113,8 @@ constructor( BouncerActionButtonModel( label = applicationContext.getString(R.string.lockscreen_emergency_call), onClick = { + // TODO(b/373930432): haptics should be played at the UI layer -> refactor + bouncerHapticPlayer.playEmergencyButtonClickFeedback() prepareToPerformAction() dozeLogger.logEmergencyCall() startEmergencyDialerActivity() @@ -119,8 +123,6 @@ constructor( onLongClick = { if (emergencyAffordanceManager.needsEmergencyAffordance()) { prepareToPerformAction() - - // TODO(b/369767936): Check that !longPressWasDragged before invoking. emergencyAffordanceManager.performEmergencyCall() } }, diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt index 49dadcefb276..12f06bbd4f5e 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt @@ -4,17 +4,21 @@ import android.view.ViewGroup import com.android.keyguard.KeyguardMessageAreaController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags import com.android.systemui.bouncer.ui.BouncerDialogFactory import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel import com.android.systemui.log.BouncerLogger import com.android.systemui.user.domain.interactor.SelectedUserInteractor import dagger.Lazy import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi /** Helper data class that allows to lazy load all the dependencies of the legacy bouncer. */ @@ -37,6 +41,10 @@ constructor( data class ComposeBouncerDependencies @Inject constructor( + @Application val applicationScope: CoroutineScope, + val keyguardInteractor: KeyguardInteractor, + val selectedUserInteractor: SelectedUserInteractor, + val legacyInteractor: PrimaryBouncerInteractor, val viewModelFactory: BouncerSceneContentViewModel.Factory, val dialogFactory: BouncerDialogFactory, val bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory, @@ -58,6 +66,10 @@ constructor( val deps = composeBouncerDependencies.get() ComposeBouncerViewBinder.bind( view, + deps.applicationScope, + deps.legacyInteractor, + deps.keyguardInteractor, + deps.selectedUserInteractor, deps.viewModelFactory, deps.dialogFactory, deps.bouncerContainerViewModelFactory, diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt index fdbc18dc9ae1..80c4291d2873 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt @@ -6,23 +6,59 @@ import androidx.activity.OnBackPressedDispatcherOwner import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.compose.ui.platform.ComposeView import androidx.lifecycle.Lifecycle +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.ui.BouncerDialogFactory import com.android.systemui.bouncer.ui.composable.BouncerContainer import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.lifecycle.WindowLifecycleState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.viewModel +import com.android.systemui.user.domain.interactor.SelectedUserInteractor +import com.android.systemui.util.kotlin.sample +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.launch /** View binder responsible for binding the compose version of the bouncer. */ object ComposeBouncerViewBinder { + private var persistentBouncerJob: Job? = null + fun bind( view: ViewGroup, + scope: CoroutineScope, + legacyInteractor: PrimaryBouncerInteractor, + keyguardInteractor: KeyguardInteractor, + selectedUserInteractor: SelectedUserInteractor, viewModelFactory: BouncerSceneContentViewModel.Factory, dialogFactory: BouncerDialogFactory, bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory, ) { + persistentBouncerJob?.cancel() + persistentBouncerJob = + scope.launch { + launch { + legacyInteractor.isShowing + .sample(keyguardInteractor.isKeyguardDismissible, ::Pair) + .collect { (isShowing, dismissible) -> + if (isShowing && dismissible) { + legacyInteractor.notifyUserRequestedBouncerWhenAlreadyAuthenticated( + selectedUserInteractor.getSelectedUserId() + ) + } + } + } + + launch { + legacyInteractor.startingDisappearAnimation.collect { + it.run() + legacyInteractor.hide() + } + } + } + view.repeatWhenAttached { view.viewModel( minWindowLifecycleState = WindowLifecycleState.ATTACHED, diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt index d6b92115c64b..837390730c7a 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt @@ -81,4 +81,11 @@ class BouncerHapticPlayer @Inject constructor(private val msdlPlayer: dagger.Laz /** Deliver MSDL feedback when a numpad key is pressed on the pin bouncer */ fun playNumpadKeyFeedback() = msdlPlayer.get().playToken(MSDLToken.KEYPRESS_STANDARD) + + /** Deliver MSDL feedback when clicking on the emergency button */ + fun playEmergencyButtonClickFeedback() { + if (isEnabled) { + msdlPlayer.get().playToken(MSDLToken.KEYPRESS_RETURN) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt index 4185aed3095c..148b9ea61e2c 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt @@ -17,7 +17,6 @@ package com.android.systemui.bouncer.ui.viewmodel import android.annotation.StringRes -import com.android.app.tracing.coroutines.flow.collectLatest import com.android.systemui.authentication.domain.interactor.AuthenticationResult import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor @@ -28,6 +27,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow sealed class AuthMethodBouncerViewModel( diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt index 5a4f8eb36673..615cb5bb51c8 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt @@ -18,10 +18,8 @@ package com.android.systemui.bouncer.ui.viewmodel import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.user.domain.interactor.SelectedUserInteractor -import com.android.systemui.util.kotlin.sample import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.awaitCancellation @@ -34,24 +32,11 @@ constructor( private val legacyInteractor: PrimaryBouncerInteractor, private val authenticationInteractor: AuthenticationInteractor, private val selectedUserInteractor: SelectedUserInteractor, - private val deviceUnlockedInteractor: DeviceUnlockedInteractor, ) : ExclusiveActivatable() { override suspend fun onActivated(): Nothing { coroutineScope { launch { - legacyInteractor.isShowing - .sample(deviceUnlockedInteractor.deviceUnlockStatus, ::Pair) - .collect { (isShowing, unlockStatus) -> - if (isShowing && unlockStatus.isUnlocked) { - legacyInteractor.notifyUserRequestedBouncerWhenAlreadyAuthenticated( - selectedUserInteractor.getSelectedUserId() - ) - } - } - } - - launch { authenticationInteractor.onAuthenticationResult.collect { authenticationSucceeded -> if (authenticationSucceeded) { legacyInteractor.notifyKeyguardAuthenticatedPrimaryAuth( @@ -60,13 +45,6 @@ constructor( } } } - - launch { - legacyInteractor.startingDisappearAnimation.collect { - it.run() - legacyInteractor.hide() - } - } awaitCancellation() } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java index 613280c3ba5d..7716ecedb772 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java @@ -17,6 +17,7 @@ package com.android.systemui.classifier; import android.content.res.Resources; +import android.hardware.devicestate.DeviceStateManager; import android.view.ViewConfiguration; import com.android.systemui.dagger.SysUISingleton; @@ -24,6 +25,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.statusbar.phone.NotificationTapHelper; +import com.android.systemui.util.Utils; import dagger.Binds; import dagger.Module; @@ -104,12 +106,8 @@ public interface FalsingModule { /** */ @Provides @Named(IS_FOLDABLE_DEVICE) - static boolean providesIsFoldableDevice(@Main Resources resources) { - try { - return resources.getIntArray( - com.android.internal.R.array.config_foldedDeviceStates).length != 0; - } catch (Resources.NotFoundException e) { - return false; - } + static boolean providesIsFoldableDevice(@Main Resources resources, + DeviceStateManager deviceStateManager) { + return Utils.isDeviceFoldable(resources, deviceStateManager); } } diff --git a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt index e3f1174d4a5f..a695163ed155 100644 --- a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt @@ -19,7 +19,7 @@ package com.android.systemui.common.usagestats.data.repository import android.app.usage.UsageEvents import android.app.usage.UsageEventsQuery import android.app.usage.UsageStatsManager -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.systemui.common.usagestats.data.model.UsageStatsQuery import com.android.systemui.common.usagestats.shared.model.ActivityEventModel import com.android.systemui.common.usagestats.shared.model.ActivityEventModel.Lifecycle diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index 08a7c395e57f..8510915d55af 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -16,6 +16,7 @@ package com.android.systemui.communal +import android.os.UserHandle import android.provider.Settings import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey @@ -147,9 +148,10 @@ constructor( .emitOnStart() .onEach { screenTimeout = - systemSettings.getInt( + systemSettings.getIntForUser( Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_TIMEOUT, + UserHandle.USER_CURRENT, ) } .launchIn(bgScope) diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 3a04d026ee84..dc248059b3d4 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -23,7 +23,7 @@ import android.content.pm.UserInfo import android.os.UserHandle import android.os.UserManager import android.provider.Settings -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt index 3826fb40ea3d..428b83ddbb3a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt @@ -16,7 +16,7 @@ package com.android.systemui.communal.domain.interactor -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt index ba96f4e56421..71bfe0c057e4 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt @@ -24,8 +24,7 @@ import android.view.ViewGroup import android.widget.FrameLayout import androidx.compose.ui.unit.IntSize import androidx.core.view.doOnLayout -import com.android.app.tracing.coroutines.flow.flowOn -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalWidgetResizing import com.android.systemui.common.ui.view.onLayoutChanged import com.android.systemui.communal.domain.model.CommunalContentModel @@ -41,6 +40,7 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn object CommunalAppWidgetHostViewBinder { private const val TAG = "CommunalAppWidgetHostViewBinder" diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt index 87fcdd7b97ee..a519649a8f1c 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.communal.ui.viewmodel import androidx.compose.foundation.gestures.AnchoredDraggableState import androidx.compose.foundation.gestures.DraggableAnchors import androidx.compose.runtime.snapshotFlow -import com.android.app.tracing.coroutines.coroutineScope +import com.android.app.tracing.coroutines.coroutineScopeTraced as coroutineScope import com.android.systemui.lifecycle.ExclusiveActivatable import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.Flow diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt index 07a7c7cba2fd..d5d3a927181b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt @@ -19,7 +19,7 @@ package com.android.systemui.communal.util import android.content.Context import android.os.Bundle import android.util.SizeF -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate import com.android.systemui.communal.widgets.CommunalAppWidgetHost diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt index 542b98896986..c894267a61dd 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt @@ -21,7 +21,7 @@ import android.app.PendingIntent import android.content.Intent import android.view.View import android.widget.RemoteViews -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.Flags.communalWidgetTrampolineFix import com.android.systemui.animation.ActivityTransitionAnimator diff --git a/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt new file mode 100644 index 000000000000..5b1c9c8b8020 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.coroutines + +import com.android.app.tracing.coroutines.createCoroutineTracingContext +import kotlin.coroutines.CoroutineContext + +fun newTracingContext(name: String): CoroutineContext { + return createCoroutineTracingContext(name) { className -> + className.startsWith("com.android.systemui.util.kotlin.JavaAdapter") || + className.startsWith("com.android.systemui.lifecycle.RepeatWhenAttached") + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 8da4d460b7a5..0de919deb943 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -50,7 +50,6 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.ImmersiveModeConfirmation import com.android.systemui.statusbar.gesture.GesturePointerEventListener import com.android.systemui.statusbar.notification.InstantAppNotifier -import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener import com.android.systemui.statusbar.policy.BatteryControllerStartable import com.android.systemui.stylus.StylusUsiPowerStartable @@ -288,11 +287,6 @@ abstract class SystemUICoreStartableModule { @Binds @IntoMap - @ClassKey(ScrimController::class) - abstract fun bindScrimController(impl: ScrimController): CoreStartable - - @Binds - @IntoMap @ClassKey(StatusBarHeadsUpChangeListener::class) abstract fun bindStatusBarHeadsUpChangeListener( impl: StatusBarHeadsUpChangeListener diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt index b8c03c071572..c464a66ea0c3 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt @@ -17,25 +17,17 @@ package com.android.systemui.deviceentry import com.android.keyguard.EmptyLockIconViewController -import com.android.keyguard.LegacyLockIconViewController import com.android.keyguard.LockIconViewController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import dagger.Lazy import dagger.Module import dagger.Provides import dagger.multibindings.Multibinds -@Module( - includes = - [ - DeviceEntryRepositoryModule::class, - FaceWakeUpTriggersConfigModule::class, - ], -) +@Module(includes = [DeviceEntryRepositoryModule::class, FaceWakeUpTriggersConfigModule::class]) abstract class DeviceEntryModule { /** * A set of DeviceEntryIconTransitions. Ensures that this can be injected even if it's empty. @@ -46,14 +38,9 @@ abstract class DeviceEntryModule { @Provides @SysUISingleton fun provideLockIconViewController( - legacyLockIconViewController: Lazy<LegacyLockIconViewController>, - emptyLockIconViewController: Lazy<EmptyLockIconViewController>, + emptyLockIconViewController: Lazy<EmptyLockIconViewController> ): LockIconViewController { - return if (DeviceEntryUdfpsRefactor.isEnabled) { - emptyLockIconViewController.get() - } else { - legacyLockIconViewController.get() - } + return emptyLockIconViewController.get() } } } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt index ffe392a52c9f..88daa5de8816 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt @@ -21,7 +21,6 @@ import android.hardware.face.FaceManager import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus import javax.inject.Inject @@ -55,9 +54,7 @@ constructor( faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>() init { - if (DeviceEntryUdfpsRefactor.isEnabled) { - startUpdatingFaceHelpMessageDeferral() - } + startUpdatingFaceHelpMessageDeferral() } /** diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt deleted file mode 100644 index b5d5803ca6fb..000000000000 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.deviceentry.shared - -import com.android.systemui.Flags -import com.android.systemui.flags.FlagToken -import com.android.systemui.flags.RefactorFlagUtils - -/** Helper for reading or using the device entry udfps refactor flag state. */ -@Suppress("NOTHING_TO_INLINE") -object DeviceEntryUdfpsRefactor { - /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR - - /** A token used for dependency declaration */ - val token: FlagToken - get() = FlagToken(FLAG_NAME, isEnabled) - - /** Is the refactor enabled */ - @JvmStatic - inline val isEnabled - get() = Flags.deviceEntryUdfpsRefactor() - - /** - * Called to ensure code is only run when the flag is enabled. This protects users from the - * unintended behaviors caused by accidentally running new logic, while also crashing on an eng - * build to ensure that the refactor author catches issues in testing. - */ - @JvmStatic - inline fun isUnexpectedlyInLegacyMode() = - RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) - - /** - * Called to ensure code is only run when the flag is disabled. This will throw an exception if - * the flag is enabled to ensure that the refactor author catches issues in testing. - */ - @JvmStatic - inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) -} diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt index fa7603ff777c..1da5351ac2a3 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt @@ -17,7 +17,14 @@ package com.android.systemui.display.data.repository import android.content.Context +import android.hardware.devicestate.DeviceState as PlatformDeviceState +import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT +import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN import android.hardware.devicestate.DeviceStateManager +import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags import com.android.internal.R import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.qualifiers.Background @@ -34,15 +41,15 @@ interface DeviceStateRepository { val state: StateFlow<DeviceState> enum class DeviceState { - /** Device state in [R.array.config_foldedDeviceStates] */ + /** Device state that corresponds to the device being folded */ FOLDED, - /** Device state in [R.array.config_halfFoldedDeviceStates] */ + /** Device state that corresponds to the device being half-folded */ HALF_FOLDED, - /** Device state in [R.array.config_openDeviceStates] */ + /** Device state in that corresponds to the device being unfolded */ UNFOLDED, - /** Device state in [R.array.config_rearDisplayDeviceStates] */ + /** Device state that corresponds to the device being in rear display mode */ REAR_DISPLAY, - /** Device state in [R.array.config_concurrentDisplayDeviceStates] */ + /** Device state in that corresponds to the device being in concurrent display mode */ CONCURRENT_DISPLAY, /** Device state in none of the other arrays. */ UNKNOWN, @@ -52,8 +59,8 @@ interface DeviceStateRepository { class DeviceStateRepositoryImpl @Inject constructor( - context: Context, - deviceStateManager: DeviceStateManager, + val context: Context, + val deviceStateManager: DeviceStateManager, @Background bgScope: CoroutineScope, @Background executor: Executor ) : DeviceStateRepository { @@ -70,11 +77,17 @@ constructor( .stateIn(bgScope, started = SharingStarted.WhileSubscribed(), DeviceState.UNKNOWN) private fun deviceStateToPosture(deviceStateId: Int): DeviceState { - return deviceStateMap.firstOrNull { (ids, _) -> deviceStateId in ids }?.deviceState - ?: DeviceState.UNKNOWN + return if (DeviceStateManagerFlags.deviceStatePropertyMigration()) { + deviceStateManager.supportedDeviceStates + .firstOrNull { it.identifier == deviceStateId } + ?.toDeviceStateEnum() ?: DeviceState.UNKNOWN + } else { + deviceStateMap.firstOrNull { (ids, _) -> deviceStateId in ids }?.deviceState + ?: DeviceState.UNKNOWN + } } - private val deviceStateMap = + private val deviceStateMap: List<IdsPerDeviceState> = listOf( R.array.config_foldedDeviceStates to DeviceState.FOLDED, R.array.config_halfFoldedDeviceStates to DeviceState.HALF_FOLDED, @@ -85,4 +98,26 @@ constructor( .map { IdsPerDeviceState(context.resources.getIntArray(it.first).toSet(), it.second) } private data class IdsPerDeviceState(val ids: Set<Int>, val deviceState: DeviceState) + + /** + * Maps a [PlatformDeviceState] to the corresponding [DeviceState] value based on the properties + * of the state. + */ + private fun PlatformDeviceState.toDeviceStateEnum(): DeviceState { + return when { + hasProperty(PROPERTY_FEATURE_REAR_DISPLAY) -> DeviceState.REAR_DISPLAY + hasProperty(PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT) -> { + DeviceState.CONCURRENT_DISPLAY + } + hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY) -> DeviceState.FOLDED + hasProperties( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY, + PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN + ) -> DeviceState.HALF_FOLDED + hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY) -> { + DeviceState.UNFOLDED + } + else -> DeviceState.UNKNOWN + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt index 30624757ebe3..e3fce0007f26 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt @@ -17,8 +17,8 @@ package com.android.systemui.display.data.repository import android.view.Display -import com.android.app.tracing.coroutines.createCoroutineTracingContext import com.android.systemui.CoreStartable +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.core.StatusBarConnectedDisplays @@ -69,7 +69,7 @@ constructor( backgroundApplicationScope } else { CoroutineScope( - backgroundDispatcher + createCoroutineTracingContext("DisplayScope$displayId") + backgroundDispatcher + newTracingContext("DisplayScope$displayId") ) } } diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt new file mode 100644 index 000000000000..2ce3e43389fa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.display.data.repository + +import android.view.Display +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.qualifiers.Background +import java.io.PrintWriter +import java.util.concurrent.ConcurrentHashMap +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +/** Provides per display instances of [T]. */ +interface PerDisplayStore<T> { + + /** + * The instance for the default/main display of the device. For example, on a phone or a tablet, + * the default display is the internal/built-in display of the device. + * + * Note that the id of the default display is [Display.DEFAULT_DISPLAY]. + */ + val defaultDisplay: T + + /** + * Returns an instance for a specific display id. + * + * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing + * displays. + */ + fun forDisplay(displayId: Int): T +} + +abstract class PerDisplayStoreImpl<T>( + @Background private val backgroundApplicationScope: CoroutineScope, + private val displayRepository: DisplayRepository, +) : PerDisplayStore<T>, CoreStartable { + + private val perDisplayInstances = ConcurrentHashMap<Int, T>() + + /** + * The instance for the default/main display of the device. For example, on a phone or a tablet, + * the default display is the internal/built-in display of the device. + * + * Note that the id of the default display is [Display.DEFAULT_DISPLAY]. + */ + override val defaultDisplay: T + get() = forDisplay(Display.DEFAULT_DISPLAY) + + /** + * Returns an instance for a specific display id. + * + * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing + * displays. + */ + override fun forDisplay(displayId: Int): T { + if (displayRepository.getDisplay(displayId) == null) { + throw IllegalArgumentException("Display with id $displayId doesn't exist.") + } + return perDisplayInstances.computeIfAbsent(displayId) { + createInstanceForDisplay(displayId) + } + } + + abstract fun createInstanceForDisplay(displayId: Int): T + + override fun start() { + val instanceType = instanceClass.simpleName + backgroundApplicationScope.launch(CoroutineName("PerDisplayStore#<$instanceType>start")) { + displayRepository.displayRemovalEvent.collect { removedDisplayId -> + val removedInstance = perDisplayInstances.remove(removedDisplayId) + removedInstance?.let { onDisplayRemovalAction(it) } + } + } + } + + abstract val instanceClass: Class<T> + + /** + * Will be called when the display associated with [instance] was removed. It allows to perform + * any clean up if needed. + */ + open suspend fun onDisplayRemovalAction(instance: T) {} + + override fun dump(pw: PrintWriter, args: Array<out String>) { + pw.println(perDisplayInstances) + } +} + +class SingleDisplayStore<T>(defaultInstance: T) : PerDisplayStore<T> { + override val defaultDisplay: T = defaultInstance + + override fun forDisplay(displayId: Int): T = defaultDisplay +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt index 3992c3fb70b0..724f1c5323cf 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt @@ -22,6 +22,7 @@ import android.service.controls.ControlsProviderService import android.service.dreams.DreamService import android.window.TaskFragmentInfo import com.android.systemui.controls.settings.ControlsSettingsRepository +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dreams.DreamLogger import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor @@ -39,7 +40,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import com.android.app.tracing.coroutines.createCoroutineTracingContext class HomeControlsDreamService @Inject @@ -54,7 +54,8 @@ constructor( ) : DreamService() { private val serviceJob = SupervisorJob() - private val serviceScope = CoroutineScope(bgDispatcher + serviceJob + createCoroutineTracingContext("HomeControlsDreamService")) + private val serviceScope = + CoroutineScope(bgDispatcher + serviceJob + newTracingContext("HomeControlsDreamService")) private val logger = DreamLogger(logBuffer, TAG) private lateinit var taskFragmentComponent: TaskFragmentComponent private val wakeLock: WakeLock by lazy { diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt index 4caf95b707b1..7fa7da192ad0 100644 --- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt +++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt @@ -16,10 +16,10 @@ package com.android.systemui.education.dagger -import com.android.app.tracing.coroutines.createCoroutineTracingContext import com.android.systemui.CoreStartable import com.android.systemui.Flags import com.android.systemui.contextualeducation.GestureType +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.education.data.repository.ContextualEducationRepository import com.android.systemui.education.data.repository.UserContextualEducationRepository @@ -57,7 +57,9 @@ interface ContextualEducationModule { fun provideEduDataStoreScope( @Background bgDispatcher: CoroutineDispatcher ): CoroutineScope { - return CoroutineScope(bgDispatcher + SupervisorJob() + createCoroutineTracingContext("EduDataStoreScope")) + return CoroutineScope( + bgDispatcher + SupervisorJob() + newTracingContext("EduDataStoreScope") + ) } @EduClock diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt index ca92953dca4a..1439ecde3bfd 100644 --- a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt @@ -22,13 +22,18 @@ import android.os.Bundle import android.view.Gravity import android.view.Window import android.view.WindowManager +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityManager import android.widget.ImageView import android.widget.TextView import com.android.systemui.education.ui.viewmodel.ContextualEduToastViewModel import com.android.systemui.res.R -class ContextualEduDialog(context: Context, private val model: ContextualEduToastViewModel) : - Dialog(context) { +class ContextualEduDialog( + context: Context, + private val model: ContextualEduToastViewModel, + private val accessibilityManager: AccessibilityManager, +) : Dialog(context) { override fun onCreate(savedInstanceState: Bundle?) { setUpWindowProperties() setWindowPosition() @@ -36,6 +41,7 @@ class ContextualEduDialog(context: Context, private val model: ContextualEduToas window?.setTitle(context.getString(R.string.contextual_education_dialog_title)) setContentView(R.layout.contextual_edu_dialog) setContent() + sendAccessibilityEvent() super.onCreate(savedInstanceState) } @@ -44,10 +50,30 @@ class ContextualEduDialog(context: Context, private val model: ContextualEduToas findViewById<ImageView>(R.id.edu_icon)?.let { it.setImageResource(model.icon) } } + private fun sendAccessibilityEvent() { + if (!accessibilityManager.isEnabled) { + return + } + + // It is a toast-like dialog which is unobtrusive and not focusable. So it needs to call + // accessibilityManager.sendAccessibilityEvent explicitly to announce the message. + accessibilityManager.sendAccessibilityEvent( + AccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT).apply { + text.add(model.message) + } + ) + } + private fun setUpWindowProperties() { window?.apply { requestFeature(Window.FEATURE_NO_TITLE) setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG) + // NOT_TOUCH_MODAL allows users to interact with background elements and NOT_FOCUSABLE + // avoids changing the existing focus when dialog is shown. + addFlags( + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + ) clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) setBackgroundDrawableResource(android.R.color.transparent) } diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt index 913ecdd4aadc..1996efa14d7c 100644 --- a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt @@ -25,6 +25,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.os.UserHandle +import android.view.accessibility.AccessibilityManager import androidx.core.app.NotificationCompat import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton @@ -64,12 +65,13 @@ constructor( context: Context, viewModel: ContextualEduViewModel, notificationManager: NotificationManager, + accessibilityManager: AccessibilityManager, ) : this( applicationScope, viewModel, context, notificationManager, - createDialog = { model -> ContextualEduDialog(context, model) }, + createDialog = { model -> ContextualEduDialog(context, model, accessibilityManager) }, ) var dialog: Dialog? = null diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt index e396767ecf8e..1dbcb3dfe399 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt @@ -22,7 +22,7 @@ import androidx.compose.foundation.interaction.InteractionSource import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.util.VelocityTracker import androidx.compose.ui.unit.Velocity -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig import com.android.systemui.haptics.slider.SliderDragVelocityProvider import com.android.systemui.haptics.slider.SliderEventType diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt index 6df8355550a0..a94df091230d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt @@ -30,7 +30,7 @@ import android.net.Uri import android.os.Binder import android.os.Bundle import android.util.Log -import com.android.app.tracing.coroutines.runBlocking +import com.android.app.tracing.coroutines.runBlockingTraced as runBlocking import com.android.systemui.SystemUIAppComponentFactoryBase import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback import com.android.systemui.dagger.qualifiers.Main diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 2f41c0b2c1ea..f549e64ca853 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -23,6 +23,7 @@ import android.app.WallpaperManager import android.content.res.Resources import android.graphics.Matrix import android.graphics.Rect +import android.hardware.devicestate.DeviceStateManager import android.os.DeadObjectException import android.os.Handler import android.os.PowerManager @@ -56,6 +57,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.Utils.isDeviceFoldable import dagger.Lazy import javax.inject.Inject @@ -169,6 +171,7 @@ constructor( private val notificationShadeWindowController: NotificationShadeWindowController, private val powerManager: PowerManager, private val wallpaperManager: WallpaperManager, + private val deviceStateManager: DeviceStateManager ) : KeyguardStateController.Callback, ISysuiUnlockAnimationController.Stub() { interface KeyguardUnlockAnimationListener { @@ -489,7 +492,7 @@ constructor( "${!notificationShadeWindowController.isLaunchingActivity}" ) Log.wtf(TAG, " launcherUnlockController != null: ${launcherUnlockController != null}") - Log.wtf(TAG, " !isFoldable(context): ${!isFoldable(resources)}") + Log.wtf(TAG, " !isFoldable(context): ${!isDeviceFoldable(resources, deviceStateManager)}") } /** @@ -1310,11 +1313,4 @@ constructor( return if (fasterUnlockTransition()) UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS else LEGACY_UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS } - - companion object { - - fun isFoldable(resources: Resources): Boolean { - return resources.getIntArray(R.array.config_foldedDeviceStates).isNotEmpty() - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 3230285fcd71..e79f5902575f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -32,6 +32,7 @@ import com.android.keyguard.KeyguardStatusView import com.android.keyguard.KeyguardStatusViewController import com.android.keyguard.dagger.KeyguardStatusViewComponent import com.android.systemui.CoreStartable +import com.android.systemui.Flags.lightRevealMigration import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.dagger.SysUISingleton @@ -42,6 +43,7 @@ import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder +import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder import com.android.systemui.keyguard.ui.composable.LockscreenContent import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea @@ -51,6 +53,7 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel +import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel import com.android.systemui.plugins.FalsingManager @@ -59,11 +62,13 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.NotificationShadeWindowView import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.KeyguardIndicationController +import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator +import com.android.systemui.wallpapers.ui.viewmodel.WallpaperViewModel import com.google.android.msdl.domain.MSDLPlayer import dagger.Lazy import java.util.Optional @@ -105,6 +110,9 @@ constructor( private val keyguardViewMediator: KeyguardViewMediator, private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>, private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, + private val lightRevealScrimViewModel: LightRevealScrimViewModel, + private val lightRevealScrim: LightRevealScrim, + private val wallpaperViewModel: WallpaperViewModel, @Main private val mainDispatcher: CoroutineDispatcher, private val msdlPlayer: MSDLPlayer, ) : CoreStartable { @@ -133,6 +141,14 @@ constructor( bindKeyguardRootView() initializeViews() + if (lightRevealMigration()) { + LightRevealScrimViewBinder.bind( + lightRevealScrim, + lightRevealScrimViewModel, + wallpaperViewModel, + ) + } + if (!SceneContainerFlag.isEnabled) { KeyguardBlueprintViewBinder.bind( keyguardRootView, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 690ae71aa56e..b7d0d453779f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -24,7 +24,7 @@ import android.annotation.SuppressLint import android.os.Trace import android.util.Log import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.shared.model.KeyguardState diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt index 0a15bbf18249..4c9c282514cd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt @@ -39,6 +39,7 @@ import kotlin.math.max import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest @@ -65,6 +66,9 @@ interface LightRevealScrimRepository { val isAnimating: Boolean + /** Limit the max alpha for the scrim to allow for some transparency */ + val maxAlpha: MutableStateFlow<Float> + fun startRevealAmountAnimator(reveal: Boolean, duration: Long = DEFAULT_REVEAL_DURATION) } @@ -79,6 +83,7 @@ constructor( ) : LightRevealScrimRepository { companion object { val TAG = LightRevealScrimRepository::class.simpleName!! + val DEFAULT_MAX_ALPHA = 1f } /** The reveal effect used if the device was locked/unlocked via the power button. */ @@ -127,6 +132,8 @@ constructor( private val revealAmountAnimator = ValueAnimator.ofFloat(0f, 1f) + override val maxAlpha: MutableStateFlow<Float> = MutableStateFlow(DEFAULT_MAX_ALPHA) + override val revealAmount: Flow<Float> = callbackFlow { val updateListener = Animator.AnimatorUpdateListener { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt index 96260770d89f..03cf1a4b3e9a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt @@ -23,15 +23,12 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor -import com.android.systemui.keyguard.data.repository.BiometricType import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -41,7 +38,6 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch /** * Encapsulates business logic for device entry events that impact the side fingerprint sensor @@ -51,7 +47,6 @@ import kotlinx.coroutines.launch class DeviceEntrySideFpsOverlayInteractor @Inject constructor( - @Application private val applicationScope: CoroutineScope, @Application private val context: Context, deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, private val sceneInteractor: SceneInteractor, @@ -60,18 +55,6 @@ constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor, ) { - init { - if (!DeviceEntryUdfpsRefactor.isEnabled) { - applicationScope.launch { - deviceEntryFingerprintAuthRepository.availableFpSensorType.collect { sensorType -> - if (sensorType == BiometricType.SIDE_FINGERPRINT) { - alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG) - } - } - } - } - } - private val isSideFpsIndicatorOnPrimaryBouncerEnabled: Boolean get() = context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer) @@ -90,7 +73,7 @@ constructor( primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(), // Bouncer scene visibility changes. isBouncerSceneActive, - deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it } + deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it }, ) .map { isBouncerActive() && diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 4cf9ec8890d4..9896365abff9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import android.util.Log import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt deleted file mode 100644 index f3bd0e9496b1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.keyguard.domain.interactor - -import android.animation.ValueAnimator -import com.android.app.animation.Interpolators -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository -import com.android.systemui.keyguard.shared.model.BiometricUnlockMode -import com.android.systemui.keyguard.shared.model.DozeStateModel -import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.power.domain.interactor.PowerInteractor -import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.util.kotlin.sample -import javax.inject.Inject -import kotlin.time.Duration.Companion.milliseconds -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch - -@SysUISingleton -class FromDreamingLockscreenHostedTransitionInteractor -@Inject -constructor( - override val transitionRepository: KeyguardTransitionRepository, - override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, - transitionInteractor: KeyguardTransitionInteractor, - @Background private val scope: CoroutineScope, - @Background bgDispatcher: CoroutineDispatcher, - @Main mainDispatcher: CoroutineDispatcher, - keyguardInteractor: KeyguardInteractor, - powerInteractor: PowerInteractor, - keyguardOcclusionInteractor: KeyguardOcclusionInteractor, -) : - TransitionInteractor( - fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - transitionInteractor = transitionInteractor, - mainDispatcher = mainDispatcher, - bgDispatcher = bgDispatcher, - powerInteractor = powerInteractor, - keyguardOcclusionInteractor = keyguardOcclusionInteractor, - keyguardInteractor = keyguardInteractor, - ) { - - override fun start() { - if (SceneContainerFlag.isEnabled) return - listenForDreamingLockscreenHostedToLockscreen() - listenForDreamingLockscreenHostedToGone() - listenForDreamingLockscreenHostedToDozing() - listenForDreamingLockscreenHostedToOccluded() - listenForDreamingLockscreenHostedToPrimaryBouncer() - } - - private fun listenForDreamingLockscreenHostedToLockscreen() { - scope.launch { - keyguardInteractor.isActiveDreamLockscreenHosted - // Add a slight delay to prevent transitioning to lockscreen from happening too soon - // as dozing can arrive in a slight gap after the lockscreen hosted dream stops. - .onEach { delay(50) } - .sample(keyguardInteractor.dozeTransitionModel, ::Pair) - .filterRelevantKeyguardStateAnd { - (isActiveDreamLockscreenHosted, dozeTransitionModel) -> - !isActiveDreamLockscreenHosted && - DozeStateModel.isDozeOff(dozeTransitionModel.to) - } - .collect { startTransitionTo(KeyguardState.LOCKSCREEN) } - } - } - - private fun listenForDreamingLockscreenHostedToOccluded() { - scope.launch { - keyguardInteractor.isActiveDreamLockscreenHosted - .sample(keyguardInteractor.isKeyguardOccluded, ::Pair) - .filterRelevantKeyguardStateAnd { (isActiveDreamLockscreenHosted, isOccluded) -> - isOccluded && !isActiveDreamLockscreenHosted - } - .collect { startTransitionTo(KeyguardState.OCCLUDED) } - } - } - - private fun listenForDreamingLockscreenHostedToPrimaryBouncer() { - scope.launch { - keyguardInteractor.primaryBouncerShowing - .filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing } - .collect { startTransitionTo(KeyguardState.PRIMARY_BOUNCER) } - } - } - - private fun listenForDreamingLockscreenHostedToGone() { - scope.launch { - keyguardInteractor.biometricUnlockState - .filterRelevantKeyguardStateAnd { biometricUnlockState -> - biometricUnlockState.mode == BiometricUnlockMode.WAKE_AND_UNLOCK_FROM_DREAM - } - .collect { startTransitionTo(KeyguardState.GONE) } - } - } - - private fun listenForDreamingLockscreenHostedToDozing() { - scope.launch { - keyguardInteractor.dozeTransitionModel - .filterRelevantKeyguardStateAnd { it.to == DozeStateModel.DOZE } - .collect { startTransitionTo(KeyguardState.DOZING) } - } - } - - override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { - return ValueAnimator().apply { - interpolator = Interpolators.LINEAR - duration = - if (toState == KeyguardState.LOCKSCREEN) TO_LOCKSCREEN_DURATION.inWholeMilliseconds - else DEFAULT_DURATION.inWholeMilliseconds - } - } - - companion object { - private val DEFAULT_DURATION = 500.milliseconds - val TO_LOCKSCREEN_DURATION = 1167.milliseconds - } -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 9a0a85823929..7b757657b1d9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -20,7 +20,7 @@ import android.animation.ValueAnimator import android.annotation.SuppressLint import android.app.DreamManager import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt index 6b6a3dce630a..a6f0db5a86aa 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index 58c8a0456241..a4a215f6eeef 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -75,7 +75,6 @@ constructor( listenForGoneToDreaming() listenForGoneToLockscreenOrHubOrOccluded() listenForGoneToOccluded() - listenForGoneToDreamingLockscreenHosted() } fun showKeyguard() { @@ -93,7 +92,7 @@ constructor( if (keyguardInteractor.isKeyguardOccluded.value) { startTransitionTo( KeyguardState.OCCLUDED, - ownerReason = "Dismissible keyguard with occlusion" + ownerReason = "Dismissible keyguard with occlusion", ) } } @@ -129,7 +128,7 @@ constructor( KeyguardState.LOCKSCREEN, ownerReason = "Keyguard was re-enabled, and we weren't GONE when it " + - "was originally disabled" + "was originally disabled", ) } } @@ -153,23 +152,10 @@ constructor( } } - private fun listenForGoneToDreamingLockscreenHosted() { - scope.launch("$TAG#listenForGoneToDreamingLockscreenHosted") { - keyguardInteractor.isActiveDreamLockscreenHosted - .filterRelevantKeyguardStateAnd { isActiveDreamLockscreenHosted -> - isActiveDreamLockscreenHosted - } - .collect { startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED) } - } - } - private fun listenForGoneToDreaming() { scope.launch("$TAG#listenForGoneToDreaming") { keyguardInteractor.isAbleToDream - .sample(keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair) - .filterRelevantKeyguardStateAnd { (isAbleToDream, isActiveDreamLockscreenHosted) -> - isAbleToDream && !isActiveDreamLockscreenHosted - } + .filterRelevantKeyguardStateAnd { isAbleToDream -> isAbleToDream } .collect { startTransitionTo(KeyguardState.DREAMING) } } } @@ -177,7 +163,7 @@ constructor( private fun listenForGoneToAodOrDozing() { scope.launch("$TAG#listenForGoneToAodOrDozing") { listenForSleepTransition( - modeOnCanceledFromStartedStep = { TransitionModeOnCanceled.RESET }, + modeOnCanceledFromStartedStep = { TransitionModeOnCanceled.RESET } ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 6d1d9cbd9aae..e84db062a5f5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import android.util.MathUtils import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.dagger.SysUISingleton @@ -135,20 +135,13 @@ constructor( .sampleCombine( internalTransitionInteractor.currentTransitionInfoInternal, transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN), - keyguardInteractor.isActiveDreamLockscreenHosted, ) - .collect { - (isAbleToDream, transitionInfo, isOnLockscreen, isActiveDreamLockscreenHosted) - -> + .collect { (isAbleToDream, transitionInfo, isOnLockscreen) -> val isTransitionInterruptible = transitionInfo.to == KeyguardState.LOCKSCREEN && !invalidFromStates.contains(transitionInfo.from) if (isAbleToDream && (isOnLockscreen || isTransitionInterruptible)) { - if (isActiveDreamLockscreenHosted) { - startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED) - } else { - startTransitionTo(KeyguardState.DREAMING) - } + startTransitionTo(KeyguardState.DREAMING) } } } @@ -390,7 +383,6 @@ constructor( TO_AOD_DURATION } KeyguardState.DOZING -> TO_DOZING_DURATION - KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> TO_DREAMING_HOSTED_DURATION KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION else -> DEFAULT_DURATION }.inWholeMilliseconds @@ -402,7 +394,6 @@ constructor( private val DEFAULT_DURATION = 400.milliseconds val TO_DOZING_DURATION = 500.milliseconds val TO_DREAMING_DURATION = 933.milliseconds - val TO_DREAMING_HOSTED_DURATION = 933.milliseconds val TO_OCCLUDED_DURATION = 550.milliseconds val TO_AOD_DURATION = 500.milliseconds val TO_AOD_FOLD_DURATION = 1100.milliseconds diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index 30babe6d47c7..0ecf7816e9b7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -78,7 +78,6 @@ constructor( listenForPrimaryBouncerToGone() listenForPrimaryBouncerToAsleep() listenForPrimaryBouncerNotShowing() - listenForPrimaryBouncerToDreamingLockscreenHosted() listenForTransitionToCamera(scope, keyguardInteractor) } @@ -108,23 +107,19 @@ constructor( if (KeyguardWmStateRefactor.isEnabled) { scope.launch { keyguardInteractor.primaryBouncerShowing - .sample( - powerInteractor.isAwake, - keyguardInteractor.isActiveDreamLockscreenHosted, - communalSceneInteractor.isIdleOnCommunal, - ) - .filterRelevantKeyguardStateAnd { (isBouncerShowing, _, _, _) -> + .sample(powerInteractor.isAwake, communalSceneInteractor.isIdleOnCommunal) + .filterRelevantKeyguardStateAnd { (isBouncerShowing, _, _) -> // TODO(b/307976454) - See if we need to listen for SHOW_WHEN_LOCKED // activities showing up over the bouncer. Camera launch can't show up over // bouncer since the first power press hides bouncer. Do occluding // activities auto hide bouncer? Not sure. !isBouncerShowing } - .collect { (_, isAwake, isActiveDreamLockscreenHosted, isIdleOnCommunal) -> + .collect { (_, isAwake, isIdleOnCommunal) -> if ( !maybeStartTransitionToOccludedOrInsecureCamera { state, reason -> startTransitionTo(state, ownerReason = reason) - } && isAwake && !isActiveDreamLockscreenHosted + } && isAwake ) { val toState = if (isIdleOnCommunal) { @@ -195,19 +190,6 @@ constructor( scope.launch { listenForSleepTransition() } } - private fun listenForPrimaryBouncerToDreamingLockscreenHosted() { - if (SceneContainerFlag.isEnabled) return - scope.launch { - keyguardInteractor.primaryBouncerShowing - .sample(keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair) - .filterRelevantKeyguardStateAnd { (isBouncerShowing, isActiveDreamLockscreenHosted) - -> - !isBouncerShowing && isActiveDreamLockscreenHosted - } - .collect { startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED) } - } - } - private fun listenForPrimaryBouncerToGone() { if (SceneContainerFlag.isEnabled) return if (KeyguardWmStateRefactor.isEnabled) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index d7f96b55c4a3..6ecbc6175e40 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -297,20 +297,39 @@ constructor( val isKeyguardVisible: Flow<Boolean> = combine(isKeyguardShowing, isKeyguardOccluded) { showing, occluded -> showing && !occluded } + /** + * Event types that affect whether secure camera is active. Only used by [isSecureCameraActive]. + */ + private enum class SecureCameraRelatedEventType { + KeyguardBecameVisible, + PrimaryBouncerBecameVisible, + SecureCameraLaunched, + } + /** Whether camera is launched over keyguard. */ - val isSecureCameraActive: Flow<Boolean> by lazy { - combine(isKeyguardVisible, primaryBouncerShowing, onCameraLaunchDetected) { - isKeyguardVisible, - isPrimaryBouncerShowing, - cameraLaunchEvent -> - when { - isKeyguardVisible -> false - isPrimaryBouncerShowing -> false - else -> cameraLaunchEvent.type == CameraLaunchType.POWER_DOUBLE_TAP + val isSecureCameraActive: Flow<Boolean> = + merge( + onCameraLaunchDetected + .filter { it.type == CameraLaunchType.POWER_DOUBLE_TAP } + .map { SecureCameraRelatedEventType.SecureCameraLaunched }, + isKeyguardVisible + .filter { it } + .map { SecureCameraRelatedEventType.KeyguardBecameVisible }, + primaryBouncerShowing + .filter { it } + .map { SecureCameraRelatedEventType.PrimaryBouncerBecameVisible }, + ) + .map { + when (it) { + SecureCameraRelatedEventType.SecureCameraLaunched -> true + // When secure camera is closed, either the keyguard or the primary bouncer will + // have to show, so those events tell us that secure camera is no longer active. + SecureCameraRelatedEventType.KeyguardBecameVisible -> false + SecureCameraRelatedEventType.PrimaryBouncerBecameVisible -> false } } .onStart { emit(false) } - } + .distinctUntilChanged() /** The approximate location on the screen of the fingerprint sensor, if one is available. */ val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index 2c3b481b9e16..26bf26b34a8a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -22,7 +22,7 @@ import android.app.admin.DevicePolicyManager import android.content.Context import android.content.Intent import android.util.Log -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.widget.LockPatternUtils import com.android.keyguard.logging.KeyguardQuickAffordancesLogger diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt index b71533389e2d..2b4582a2a096 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt @@ -48,7 +48,6 @@ constructor( is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it") is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it") is FromAlternateBouncerTransitionInteractor -> Log.d(TAG, "Started $it") - is FromDreamingLockscreenHostedTransitionInteractor -> Log.d(TAG, "Started $it") } it.start() } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt index 1497026b5750..cf747c81769a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.keyguard.domain.interactor @@ -21,17 +22,22 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_DURATION import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.LightRevealEffect import com.android.systemui.util.kotlin.sample import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch @SysUISingleton @@ -39,7 +45,7 @@ class LightRevealScrimInteractor @Inject constructor( private val transitionInteractor: KeyguardTransitionInteractor, - private val lightRevealScrimRepository: LightRevealScrimRepository, + private val repository: LightRevealScrimRepository, @Application private val scope: CoroutineScope, private val scrimLogger: ScrimLogger, private val powerInteractor: Lazy<PowerInteractor>, @@ -63,17 +69,17 @@ constructor( DEFAULT_REVEAL_DURATION } - lightRevealScrimRepository.startRevealAmountAnimator( + repository.startRevealAmountAnimator( willBeRevealedInState(it.to), - duration = animationDuration + duration = animationDuration, ) } } } private val isLastSleepDueToFold: Boolean - get() = powerInteractor.get().detailedWakefulness.value - .lastSleepReason == WakeSleepReason.FOLD + get() = + powerInteractor.get().detailedWakefulness.value.lastSleepReason == WakeSleepReason.FOLD /** * Whenever a keyguard transition starts, sample the latest reveal effect from the repository @@ -86,12 +92,29 @@ constructor( * LiftReveal. */ val lightRevealEffect: Flow<LightRevealEffect> = - transitionInteractor.startedKeyguardTransitionStep.sample( - lightRevealScrimRepository.revealEffect - ) + transitionInteractor.startedKeyguardTransitionStep.sample(repository.revealEffect) + + /** Limit the max alpha for the scrim to allow for some transparency */ + val maxAlpha: Flow<Float> = + transitionInteractor + .isInTransition( + edge = Edge.create(Scenes.Gone, KeyguardState.AOD), + edgeWithoutSceneContainer = Edge.create(KeyguardState.GONE, KeyguardState.AOD), + ) + .flatMapLatest { isInTransition -> + // During GONE->AOD transitions, the home screen and wallpaper are still visible + // until + // WM is told to hide them, which occurs at the end of the animation. Use an opaque + // scrim until this transition is complete + if (isInTransition) { + flowOf(1f) + } else { + repository.maxAlpha + } + } val revealAmount = - lightRevealScrimRepository.revealAmount.filter { + repository.revealAmount.filter { // When the screen is off we do not want to keep producing frames as this is causing // (invisible) jank. However, we need to still pass through 1f and 0f to ensure that the // correct end states are respected even if the screen turned off (or was still off) @@ -104,7 +127,17 @@ constructor( powerInteractor.get().screenPowerState.value != ScreenPowerState.SCREEN_TURNING_ON val isAnimating: Boolean - get() = lightRevealScrimRepository.isAnimating + get() = repository.isAnimating + + /** If the wallpaper supports ambient mode, allow partial transparency */ + fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) { + repository.maxAlpha.value = + if (supportsAmbientMode) { + 0.7f + } else { + 1f + } + } /** * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt index 3c661861efd2..f976800fe1d0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt @@ -58,12 +58,6 @@ abstract class StartKeyguardTransitionModule { @Binds @IntoSet - abstract fun fromDreamingLockscreenHosted( - impl: FromDreamingLockscreenHostedTransitionInteractor - ): TransitionInteractor - - @Binds - @IntoSet abstract fun fromOccluded(impl: FromOccludedTransitionInteractor): TransitionInteractor @Binds diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt index fe5f632c0b6a..23b7b664d4f1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.binder import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.AuthKeyguardMessageArea import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt index 7ca2c2004452..6ef9863f1112 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt @@ -21,7 +21,7 @@ import android.content.res.ColorStateList import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt index 1891af2d04b0..7a368999ecc9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt @@ -29,7 +29,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt index a3f3342b9a6c..6c104a0a2470 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -28,7 +28,7 @@ import androidx.compose.ui.graphics.toArgb import androidx.core.view.isInvisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.common.ui.view.LongPressHandlingView import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt index 0470e086ad27..5bad0168fe05 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt @@ -22,7 +22,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.shared.model.KeyguardBlueprint diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index 660a650fb916..3bdf7dac75b3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -36,7 +36,7 @@ import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.settingslib.Utils import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.Expandable diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt index ba94f45528c7..8b947a3bcb1e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt @@ -22,7 +22,7 @@ import android.view.ViewGroup import android.widget.TextView import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt index 76d3389c25b4..2fd9818a38f0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt @@ -22,7 +22,7 @@ import android.view.accessibility.AccessibilityNodeInfo import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.common.ui.view.LongPressHandlingView import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt index 2d225562081a..21d1db4a5052 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt @@ -33,7 +33,7 @@ import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.policy.SystemBarUtils import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.shared.model.ClockSizeSetting @@ -129,15 +129,19 @@ object KeyguardPreviewClockViewBinder { constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0) val largeClockTopMargin = SystemBarUtils.getStatusBarHeight(context) + - context.resources.getDimensionPixelSize( - customR.dimen.small_clock_padding_top - ) + + context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) + context.resources.getDimensionPixelSize( R.dimen.keyguard_smartspace_top_offset ) + getDimen(context, DATE_WEATHER_VIEW_HEIGHT) + getDimen(context, ENHANCED_SMARTSPACE_HEIGHT) - connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin) + connect( + customR.id.lockscreen_clock_view_large, + TOP, + PARENT_ID, + TOP, + largeClockTopMargin, + ) connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START) connect( customR.id.lockscreen_clock_view_large, @@ -146,12 +150,11 @@ object KeyguardPreviewClockViewBinder { ConstraintSet.END, ) - // In preview, we'll show UDFPS icon for UDFPS devices and nothing for non-UDFPS // devices, but we need position of device entry icon to constrain clock if (getConstraint(lockId) != null) { connect(customR.id.lockscreen_clock_view_large, BOTTOM, lockId, TOP) - } else { + } else { // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection val bottomPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt index 4b75b804e52b..baa681282a0b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt @@ -22,7 +22,7 @@ import android.view.View import androidx.core.view.isInvisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.shared.model.ClockSizeSetting import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt index 27dd18d2b24e..cfd6481234ed 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt @@ -30,7 +30,7 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.logging.KeyguardQuickAffordancesLogger import com.android.settingslib.Utils import com.android.systemui.animation.Expandable diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index ea70fd02eed5..89bca0c6a373 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -39,7 +39,7 @@ import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD import com.android.keyguard.AuthInteractionProperties diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt index 4150ceb8aa31..79360370d937 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt @@ -22,7 +22,7 @@ import android.view.View import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.common.ui.binder.TextViewBinder diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt index 8b74f5dc791d..de4a1b03203c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt @@ -21,7 +21,7 @@ import androidx.constraintlayout.helper.widget.Layer import androidx.constraintlayout.widget.ConstraintLayout import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt index fd27dc39299b..3934917d13c4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.binder -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager import com.android.systemui.keyguard.ui.viewmodel.KeyguardSurfaceBehindViewModel import kotlinx.coroutines.CoroutineScope diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt index b2ee68967878..32757ce82c69 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt @@ -16,21 +16,49 @@ package com.android.systemui.keyguard.ui.binder +import android.animation.ValueAnimator import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.shared.Flags.ambientAod import com.android.systemui.statusbar.LightRevealScrim +import com.android.systemui.wallpapers.ui.viewmodel.WallpaperViewModel import kotlinx.coroutines.launch object LightRevealScrimViewBinder { @JvmStatic - fun bind(revealScrim: LightRevealScrim, viewModel: LightRevealScrimViewModel) { + fun bind( + revealScrim: LightRevealScrim, + viewModel: LightRevealScrimViewModel, + wallpaperViewModel: WallpaperViewModel, + ) { revealScrim.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { + if (ambientAod()) { + launch("$TAG#wallpaperViewModel.wallpaperSupportsAmbientMode") { + wallpaperViewModel.wallpaperSupportsAmbientMode.collect { + viewModel.setWallpaperSupportsAmbientMode(it) + } + } + launch("$TAG#viewModel.maxAlpha") { + viewModel.maxAlpha.collect { alpha -> + if (alpha != revealScrim.alpha) { + ValueAnimator.ofFloat(revealScrim.alpha, alpha).apply { + duration = 400 + addUpdateListener { animation -> + revealScrim.alpha = animation.getAnimatedValue() as Float + } + start() + } + } + } + } + } + launch("$TAG#viewModel.revealAmount") { - viewModel.revealAmount.collect { amount -> revealScrim.revealAmount = amount } + viewModel.revealAmount.collect { revealScrim.revealAmount = it } } launch("$TAG#viewModel.lightRevealEffect") { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt index ae46dd3a6ef3..b1ce47ee79e6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.binder -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager import com.android.systemui.keyguard.ui.viewmodel.WindowManagerLockscreenVisibilityViewModel import kotlinx.coroutines.CoroutineScope diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index dd8980dabb23..08d35a72ab12 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -47,7 +47,6 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.core.view.isInvisible -import com.android.app.tracing.coroutines.createCoroutineTracingContext import com.android.internal.policy.SystemBarUtils import com.android.keyguard.ClockEventController import com.android.keyguard.KeyguardClockSwitch @@ -57,6 +56,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -189,7 +189,7 @@ constructor( CoroutineScope( applicationScope.coroutineContext + Job() + - createCoroutineTracingContext("KeyguardPreviewRenderer") + newTracingContext("KeyguardPreviewRenderer") ) disposables += DisposableHandle { coroutineScope.cancel() } clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData()) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt index 075a1d2893f7..9355200daec7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt @@ -25,7 +25,7 @@ import android.os.Messenger import android.util.ArrayMap import android.util.Log import androidx.annotation.VisibleForTesting -import com.android.app.tracing.coroutines.runBlocking +import com.android.app.tracing.coroutines.runBlockingTraced as runBlocking import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt deleted file mode 100644 index 57ed455e7b13..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.keyguard.ui.viewmodel - -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.shared.model.Edge -import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED -import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN -import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow -import javax.inject.Inject -import kotlin.time.Duration.Companion.milliseconds -import kotlinx.coroutines.flow.Flow - -@SysUISingleton -class DreamingHostedToLockscreenTransitionViewModel -@Inject -constructor( - animationFlow: KeyguardTransitionAnimationFlow, -) { - - private val transitionAnimation = - animationFlow.setup( - duration = TO_LOCKSCREEN_DURATION, - edge = Edge.create(from = DREAMING_LOCKSCREEN_HOSTED, to = LOCKSCREEN), - ) - - val shortcutsAlpha: Flow<Float> = - transitionAnimation.sharedFlow( - duration = 250.milliseconds, - onStep = { it }, - onCancel = { 0f }, - ) -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt deleted file mode 100644 index 627f0de696d7..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.keyguard.ui.viewmodel - -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION -import com.android.systemui.keyguard.shared.model.Edge -import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED -import com.android.systemui.keyguard.shared.model.KeyguardState.GONE -import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow -import com.android.systemui.scene.shared.model.Scenes -import javax.inject.Inject -import kotlin.time.Duration.Companion.milliseconds -import kotlinx.coroutines.flow.Flow - -/** - * Breaks down GONE->DREAMING_LOCKSCREEN_HOSTED transition into discrete steps for corresponding - * views to consume. - */ -@SysUISingleton -class GoneToDreamingLockscreenHostedTransitionViewModel -@Inject -constructor( - animationFlow: KeyguardTransitionAnimationFlow, -) { - - private val transitionAnimation = - animationFlow - .setup( - duration = TO_DREAMING_DURATION, - edge = Edge.create(from = Scenes.Gone, to = DREAMING_LOCKSCREEN_HOSTED), - ) - .setupWithoutSceneContainer( - edge = Edge.create(from = GONE, to = DREAMING_LOCKSCREEN_HOSTED), - ) - - /** Lockscreen views alpha - hide immediately */ - val lockscreenAlpha: Flow<Float> = - transitionAnimation.sharedFlow( - duration = 1.milliseconds, - onStep = { 0f }, - ) -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt index 0d55709e94d6..df3c78232b94 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.annotation.VisibleForTesting import com.android.app.tracing.FlowTracing.traceEmissionCount +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor @@ -54,7 +55,6 @@ constructor( shadeInteractor: ShadeInteractor, aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel, dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel, - dreamingHostedToLockscreenTransitionViewModel: DreamingHostedToLockscreenTransitionViewModel, dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel, goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel, occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel, @@ -63,7 +63,6 @@ constructor( glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel, lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel, lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel, - lockscreenToDreamingHostedTransitionViewModel: LockscreenToDreamingHostedTransitionViewModel, lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel, lockscreenToGoneTransitionViewModel: LockscreenToGoneTransitionViewModel, lockscreenToOccludedTransitionViewModel: LockscreenToOccludedTransitionViewModel, @@ -89,10 +88,7 @@ constructor( /** The only time the expansion is important is while lockscreen is actively displayed */ private val shadeExpansionAlpha = - combine( - showingLockscreen, - shadeInteractor.anyExpansion, - ) { showingLockscreen, expansion -> + combine(showingLockscreen, shadeInteractor.anyExpansion) { showingLockscreen, expansion -> if (showingLockscreen) { 1 - expansion } else { @@ -112,7 +108,6 @@ constructor( merge( aodToLockscreenTransitionViewModel.shortcutsAlpha, dozingToLockscreenTransitionViewModel.shortcutsAlpha, - dreamingHostedToLockscreenTransitionViewModel.shortcutsAlpha, dreamingToLockscreenTransitionViewModel.shortcutsAlpha, goneToLockscreenTransitionViewModel.shortcutsAlpha, occludedToLockscreenTransitionViewModel.shortcutsAlpha, @@ -126,7 +121,6 @@ constructor( merge( lockscreenToAodTransitionViewModel.shortcutsAlpha, lockscreenToDozingTransitionViewModel.shortcutsAlpha, - lockscreenToDreamingHostedTransitionViewModel.shortcutsAlpha, lockscreenToDreamingTransitionViewModel.shortcutsAlpha, lockscreenToGoneTransitionViewModel.shortcutsAlpha, lockscreenToOccludedTransitionViewModel.shortcutsAlpha, @@ -137,10 +131,8 @@ constructor( /** The source of truth of alpha for all of the quick affordances on lockscreen */ val transitionAlpha: Flow<Float> = - merge( - fadeInAlpha, - fadeOutAlpha, - ) + merge(fadeInAlpha, fadeOutAlpha) + .flowName("transitionAlpha") .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), @@ -323,9 +315,7 @@ constructor( slotId = slotId, ) is KeyguardQuickAffordanceModel.Hidden -> - KeyguardQuickAffordanceViewModel( - slotId = slotId, - ) + KeyguardQuickAffordanceViewModel(slotId = slotId) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt index 82f40bf3a16a..af6cd166479a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt @@ -27,7 +27,14 @@ import kotlinx.coroutines.flow.Flow * draw a gradient that reveals/hides the contents of the screen. */ @OptIn(ExperimentalCoroutinesApi::class) -class LightRevealScrimViewModel @Inject constructor(interactor: LightRevealScrimInteractor) { +class LightRevealScrimViewModel +@Inject +constructor(private val interactor: LightRevealScrimInteractor) { val lightRevealEffect: Flow<LightRevealEffect> = interactor.lightRevealEffect val revealAmount: Flow<Float> = interactor.revealAmount + val maxAlpha: Flow<Float> = interactor.maxAlpha + + fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) { + interactor.setWallpaperSupportsAmbientMode(supportsAmbientMode) + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt deleted file mode 100644 index 778dbed90ec1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.keyguard.ui.viewmodel - -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION -import com.android.systemui.keyguard.shared.model.Edge -import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED -import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN -import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow -import javax.inject.Inject -import kotlin.time.Duration.Companion.milliseconds -import kotlinx.coroutines.flow.Flow - -@SysUISingleton -class LockscreenToDreamingHostedTransitionViewModel -@Inject -constructor( - animationFlow: KeyguardTransitionAnimationFlow, -) { - - private val transitionAnimation = - animationFlow.setup( - duration = TO_DREAMING_HOSTED_DURATION, - edge = Edge.create(from = LOCKSCREEN, to = DREAMING_LOCKSCREEN_HOSTED), - ) - - val shortcutsAlpha: Flow<Float> = - transitionAnimation.sharedFlow( - duration = 250.milliseconds, - onStep = { 1 - it }, - onFinish = { 0f }, - onCancel = { 1f }, - ) -} diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt index df1394bbfa5f..7c02f28c0696 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt @@ -19,7 +19,7 @@ package com.android.systemui.lifecycle import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.StateFactoryMarker -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.app.tracing.coroutines.traceCoroutine import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt index 555969859a1f..a86bfb18fb70 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt @@ -17,7 +17,6 @@ package com.android.systemui.lifecycle -import android.os.Trace import android.view.View import android.view.ViewTreeObserver import androidx.annotation.MainThread @@ -25,11 +24,8 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.lifecycleScope -import com.android.app.tracing.coroutines.createCoroutineTracingContext -import com.android.app.tracing.coroutines.traceCoroutine -import com.android.systemui.Flags.coroutineTracing +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.util.Assert -import com.android.systemui.util.Compile import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import kotlin.coroutines.CoroutineContext @@ -83,25 +79,13 @@ fun View.repeatWhenAttached( // default behavior. Instead, we want it to run on the view's UI thread since the user will // presumably want to call view methods that require being called from said UI thread. val lifecycleCoroutineContext = MAIN_DISPATCHER_SINGLETON + coroutineContext - val traceName = - if (Compile.IS_DEBUG && coroutineTracing()) { - inferTraceSectionName() - } else { - DEFAULT_TRACE_NAME - } var lifecycleOwner: ViewLifecycleOwner? = null val onAttachListener = object : View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) { Assert.isMainThread() lifecycleOwner?.onDestroy() - lifecycleOwner = - createLifecycleOwnerAndRun( - traceName, - view, - lifecycleCoroutineContext, - block, - ) + lifecycleOwner = createLifecycleOwnerAndRun(view, lifecycleCoroutineContext, block) } override fun onViewDetachedFromWindow(v: View) { @@ -112,13 +96,7 @@ fun View.repeatWhenAttached( addOnAttachStateChangeListener(onAttachListener) if (view.isAttachedToWindow) { - lifecycleOwner = - createLifecycleOwnerAndRun( - traceName, - view, - lifecycleCoroutineContext, - block, - ) + lifecycleOwner = createLifecycleOwnerAndRun(view, lifecycleCoroutineContext, block) } return DisposableHandle { @@ -131,14 +109,15 @@ fun View.repeatWhenAttached( } private fun createLifecycleOwnerAndRun( - nameForTrace: String, view: View, coroutineContext: CoroutineContext, block: suspend LifecycleOwner.(View) -> Unit, ): ViewLifecycleOwner { return ViewLifecycleOwner(view).apply { onCreate() - lifecycleScope.launch(coroutineContext) { traceCoroutine(nameForTrace) { block(view) } } + // TODO(b/370595466): Refactor to support installing CoroutineTracingContext on the + // top-level CoroutineScope used as the lifecycleScope + lifecycleScope.launch(coroutineContext) { block(view) } } } @@ -167,9 +146,7 @@ private fun createLifecycleOwnerAndRun( * └───────────────┴───────────────────┴──────────────┴─────────────────┘ * ``` */ -class ViewLifecycleOwner( - private val view: View, -) : LifecycleOwner { +class ViewLifecycleOwner(private val view: View) : LifecycleOwner { private val windowVisibleListener = ViewTreeObserver.OnWindowVisibilityChangeListener { updateState() } @@ -205,28 +182,6 @@ class ViewLifecycleOwner( } } -private fun isFrameInteresting(frame: StackWalker.StackFrame): Boolean = - frame.className != CURRENT_CLASS_NAME && frame.className != JAVA_ADAPTER_CLASS_NAME - -/** Get a name for the trace section include the name of the call site. */ -private fun inferTraceSectionName(): String { - try { - Trace.traceBegin(Trace.TRACE_TAG_APP, "RepeatWhenAttachedKt#inferTraceSectionName") - val interestingFrame = - StackWalker.getInstance().walk { stream -> - stream.filter(::isFrameInteresting).limit(5).findFirst() - } - return if (interestingFrame.isPresent) { - val f = interestingFrame.get() - "${f.className}#${f.methodName}:${f.lineNumber} [$DEFAULT_TRACE_NAME]" - } else { - DEFAULT_TRACE_NAME - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_APP) - } -} - /** * Runs the given [block] in a new coroutine when `this` [View]'s Window's [WindowLifecycleState] is * at least at [state] (or immediately after calling this function if the window is already at least @@ -368,8 +323,7 @@ private val ViewTreeObserver.isWindowVisible * an extension function, and plumbing dagger-injected instances for static usage has little * benefit. */ -private val MAIN_DISPATCHER_SINGLETON = - Dispatchers.Main + createCoroutineTracingContext("RepeatWhenAttached") +private val MAIN_DISPATCHER_SINGLETON = Dispatchers.Main + newTracingContext("RepeatWhenAttached") private const val DEFAULT_TRACE_NAME = "repeatWhenAttached" private const val CURRENT_CLASS_NAME = "com.android.systemui.lifecycle.RepeatWhenAttachedKt" private const val JAVA_ADAPTER_CLASS_NAME = "com.android.systemui.util.kotlin.JavaAdapterKt" diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt index 544dbddeb3f0..f40ad065e5fc 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt @@ -16,13 +16,13 @@ package com.android.systemui.mediaprojection.appselector -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.app.Activity import android.content.ComponentName import android.content.Context import android.os.UserHandle import androidx.lifecycle.DefaultLifecycleObserver import com.android.launcher3.icons.IconFactory +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader @@ -134,7 +134,11 @@ interface MediaProjectionAppSelectorModule { @MediaProjectionAppSelector @MediaProjectionAppSelectorScope fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope = - CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("MediaProjectionAppSelectorScope")) + CoroutineScope( + applicationScope.coroutineContext + + SupervisorJob() + + newTracingContext("MediaProjectionAppSelectorScope") + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java index c3729c0dcdfd..212da9ffb9c5 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java @@ -122,8 +122,11 @@ public class MediaProjectionPermissionActivity extends Activity { final Intent launchingIntent = getIntent(); mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra( EXTRA_USER_REVIEW_GRANTED_CONSENT, false); - - mPackageName = getCallingPackage(); + if (com.android.systemui.Flags.mediaProjectionRequestAttributionFix()) { + mPackageName = getLaunchedFromPackage(); + } else { + mPackageName = getCallingPackage(); + } // This activity is launched directly by an app, or system server. System server provides // the package name through the intent if so. diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java index 5e8c2c9844ee..b019c136b6ca 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java @@ -24,6 +24,7 @@ import static com.android.wm.shell.Flags.enableTaskbarOnPhones; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.RemoteException; @@ -58,6 +59,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.util.Utils; import com.android.systemui.util.settings.SecureSettings; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.pip.Pip; @@ -128,7 +130,8 @@ public class NavigationBarControllerImpl implements Optional<Pip> pipOptional, Optional<BackAnimation> backAnimation, SecureSettings secureSettings, - DisplayTracker displayTracker) { + DisplayTracker displayTracker, + DeviceStateManager deviceStateManager) { mContext = context; mExecutor = mainExecutor; mNavigationBarComponentFactory = navigationBarComponentFactory; @@ -146,11 +149,19 @@ public class NavigationBarControllerImpl implements dumpManager, autoHideController, lightBarController, pipOptional, backAnimation.orElse(null), taskStackChangeListeners); mIsLargeScreen = isLargeScreen(mContext); - mIsPhone = - mContext.getResources().getIntArray(R.array.config_foldedDeviceStates).length == 0; + mIsPhone = determineIfPhone(mContext, deviceStateManager); dumpManager.registerDumpable(this); } + private boolean determineIfPhone(Context context, DeviceStateManager deviceStateManager) { + if (android.hardware.devicestate.feature.flags.Flags.deviceStatePropertyMigration()) { + return !Utils.isDeviceFoldable(context.getResources(), deviceStateManager); + } else { + return context.getResources().getIntArray(R.array.config_foldedDeviceStates).length + == 0; + } + } + @Override public void onConfigChanged(Configuration newConfig) { boolean isOldConfigLargeScreen = mIsLargeScreen; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt index 96386e520d5a..0166176721c3 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt @@ -16,7 +16,6 @@ package com.android.systemui.navigationbar.gestural.domain -import com.android.app.tracing.coroutines.flow.flowOn import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -35,6 +34,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index 3aa9daac4866..d0f6f7961889 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -37,7 +37,7 @@ import android.os.UserManager import android.provider.Settings import android.widget.Toast import androidx.annotation.VisibleForTesting -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt index 9c5231d716da..49b44cb95e46 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -23,7 +23,9 @@ import android.graphics.Rect import android.os.Bundle import android.util.IndentingPrintWriter import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View +import android.view.ViewConfiguration import android.view.ViewGroup import android.widget.FrameLayout import androidx.activity.OnBackPressedDispatcher @@ -35,6 +37,7 @@ import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -43,6 +46,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect @@ -83,6 +87,7 @@ import com.android.systemui.Dumpable import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dump.DumpManager import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.lifecycle.setSnapshotBinding import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule.QS_PANEL @@ -145,6 +150,7 @@ constructor( private val qqsVisible = MutableStateFlow(false) private val qqsPositionOnRoot = Rect() private val composeViewPositionOnScreen = Rect() + private val scrollState = ScrollState(0) // Inside object for namespacing private val notificationScrimClippingParams = @@ -210,6 +216,9 @@ constructor( context, { notificationScrimClippingParams.isEnabled }, { notificationScrimClippingParams.params.top }, + // Only allow scrolling when we are fully expanded. That way, we don't intercept + // swipes in lockscreen (when somehow QS is receiving touches). + { scrollState.canScrollForward && viewModel.expansionState.value.progress >= 1f }, ) frame.addView( composeView, @@ -488,12 +497,8 @@ constructor( private fun setListenerCollections() { lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - // TODO - // setListenerJob( - // scrollListener, - // - // ) + this@QSFragmentCompose.view?.setSnapshotBinding { + scrollListener.value?.onQsPanelScrollChanged(scrollState.value) } launch { setListenerJob( @@ -528,6 +533,7 @@ constructor( viewModel.containerViewModel.quickQuickSettingsViewModel.squishinessViewModel .squishiness .collectAsStateWithLifecycle() + Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) { Box( modifier = @@ -591,7 +597,12 @@ constructor( modifier = Modifier.element(ElementKeys.QuickSettingsContent).fillMaxSize().weight(1f) ) { - Column { + DisposableEffect(Unit) { + lifecycleScope.launch { scrollState.scrollTo(0) } + onDispose { lifecycleScope.launch { scrollState.scrollTo(0) } } + } + + Column(modifier = Modifier.verticalScroll(scrollState)) { Spacer( modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() } ) @@ -601,15 +612,14 @@ constructor( ) } } - QuickSettingsTheme { - FooterActions( - viewModel = viewModel.footerActionsViewModel, - qsVisibilityLifecycleOwner = this@QSFragmentCompose, - modifier = - Modifier.sysuiResTag("qs_footer_actions") - .element(ElementKeys.FooterActions), - ) - } + } + QuickSettingsTheme { + FooterActions( + viewModel = viewModel.footerActionsViewModel, + qsVisibilityLifecycleOwner = this@QSFragmentCompose, + modifier = + Modifier.sysuiResTag("qs_footer_actions").element(ElementKeys.FooterActions), + ) } } } @@ -791,13 +801,17 @@ private class ExpansionTransition(currentProgress: Float) : private const val EDIT_MODE_TIME_MILLIS = 500 /** - * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as - * per [clippingEnabledProvider]. + * Performs different touch handling based on the state of the ComposeView: + * * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as + * per [clippingEnabledProvider]. + * * Intercept touches that would overscroll QS forward and instead allow them to be used to close + * the shade. */ private class FrameLayoutTouchPassthrough( context: Context, private val clippingEnabledProvider: () -> Boolean, private val clippingTopProvider: () -> Int, + private val canScrollForwardQs: () -> Boolean, ) : FrameLayout(context) { override fun isTransformedTouchPointInView( x: Float, @@ -811,4 +825,32 @@ private class FrameLayoutTouchPassthrough( super.isTransformedTouchPointInView(x, y, child, outLocalPoint) } } + + val touchSlop = ViewConfiguration.get(context).scaledTouchSlop + var downY = 0f + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + // If there's a touch on this view and we can scroll down, we don't want to be intercepted + val action = ev.actionMasked + + when (action) { + MotionEvent.ACTION_DOWN -> { + // If we can scroll down, make sure none of our parents intercepts us. + if (canScrollForwardQs()) { + parent?.requestDisallowInterceptTouchEvent(true) + } + downY = ev.y + } + + MotionEvent.ACTION_MOVE -> { + val y = ev.y.toInt() + val yDiff: Float = y - downY + if (yDiff < -touchSlop && !canScrollForwardQs()) { + // Intercept touches that are overscrolling. + return true + } + } + } + return super.onInterceptTouchEvent(ev) + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt index 26b2e2b7bd66..424be90ba2ec 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt @@ -38,6 +38,6 @@ constructor( ) { val rows = configurationRepository.onConfigurationChange.emitOnStart().map { - resources.getInteger(R.integer.quick_settings_max_rows) + resources.getInteger(R.integer.quick_settings_paginated_grid_num_rows) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt index 082f622248a1..a9205c27216d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt @@ -19,40 +19,31 @@ package com.android.systemui.qs.panels.data.repository import android.content.res.Resources import com.android.systemui.common.ui.data.repository.ConfigurationRepository import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.res.R -import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.util.kotlin.emitOnStart import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.flow.stateIn @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class QSColumnsRepository @Inject constructor( - @Application scope: CoroutineScope, @Main private val resources: Resources, configurationRepository: ConfigurationRepository, ) { - val columns: StateFlow<Int> = - if (DualShade.isEnabled) { - flowOf(resources.getInteger(R.integer.quick_settings_dual_shade_num_columns)) - } else { - configurationRepository.onConfigurationChange.emitOnStart().mapLatest { - resources.getInteger(R.integer.quick_settings_infinite_grid_num_columns) - } - } - .stateIn( - scope, - SharingStarted.WhileSubscribed(), - resources.getInteger(R.integer.quick_settings_infinite_grid_num_columns), - ) + val splitShadeColumns: Flow<Int> = + flowOf(resources.getInteger(R.integer.quick_settings_split_shade_num_columns)) + val dualShadeColumns: Flow<Int> = + flowOf(resources.getInteger(R.integer.quick_settings_dual_shade_num_columns)) + val columns: Flow<Int> = + configurationRepository.onConfigurationChange.emitOnStart().mapLatest { + resources.getInteger(R.integer.quick_settings_infinite_grid_num_columns) + } + val defaultColumns: Int = + resources.getInteger(R.integer.quick_settings_infinite_grid_num_columns) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt index f7c71ceb9e6c..ee0cfb304db0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt @@ -34,6 +34,6 @@ constructor( ) { val rows = configurationRepository.onConfigurationChange.emitOnStart().map { - resources.getInteger(R.integer.quick_qs_panel_max_rows) + resources.getInteger(R.integer.quick_qs_paginated_grid_num_rows) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt index 9b45c568dcad..11ea60e874b3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt @@ -17,11 +17,35 @@ package com.android.systemui.qs.panels.domain.interactor import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.qs.panels.data.repository.QSColumnsRepository +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.shade.shared.model.ShadeMode import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.stateIn @SysUISingleton -class QSColumnsInteractor @Inject constructor(repo: QSColumnsRepository) { - val columns: StateFlow<Int> = repo.columns +class QSColumnsInteractor +@Inject +constructor( + @Application scope: CoroutineScope, + repo: QSColumnsRepository, + shadeInteractor: ShadeInteractor, +) { + @OptIn(ExperimentalCoroutinesApi::class) + val columns: StateFlow<Int> = + shadeInteractor.shadeMode + .flatMapLatest { + when (it) { + ShadeMode.Dual -> repo.dualShadeColumns + ShadeMode.Split -> repo.splitShadeColumns + ShadeMode.Single -> repo.columns + } + } + .stateIn(scope, SharingStarted.WhileSubscribed(), repo.defaultColumns) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt new file mode 100644 index 000000000000..b9994d7bb821 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.ui.compose + +import com.android.compose.animation.Bounceable +import com.android.systemui.qs.panels.shared.model.SizedTile +import com.android.systemui.qs.panels.ui.model.GridCell +import com.android.systemui.qs.panels.ui.model.TileGridCell +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel +import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel + +data class BounceableInfo( + val bounceable: BounceableTileViewModel, + val previousTile: Bounceable?, + val nextTile: Bounceable?, + val bounceEnd: Boolean, +) + +fun List<Pair<GridCell, BounceableTileViewModel>>.bounceableInfo( + index: Int, + columns: Int, +): BounceableInfo { + val cell = this[index].first as TileGridCell + // Only look for neighbor bounceables if they are on the same row + val onLastColumn = cell.onLastColumn(cell.column, columns) + val previousTile = getOrNull(index - 1)?.takeIf { cell.column != 0 } + val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn } + return BounceableInfo(this[index].second, previousTile?.second, nextTile?.second, !onLastColumn) +} + +fun List<BounceableTileViewModel>.bounceableInfo( + sizedTile: SizedTile<TileViewModel>, + index: Int, + column: Int, + columns: Int, +): BounceableInfo { + // Only look for neighbor bounceables if they are on the same row + val onLastColumn = sizedTile.onLastColumn(column, columns) + val previousTile = getOrNull(index - 1)?.takeIf { column != 0 } + val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn } + return BounceableInfo(this[index], previousTile, nextTile, !onLastColumn) +} + +private fun <T> SizedTile<T>.onLastColumn(column: Int, columns: Int): Boolean { + return column == columns - width +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt index 770fd785723a..74fa0fef21d7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt @@ -116,9 +116,9 @@ class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>, private val c regenerateGrid() _tiles.add(insertionIndex.coerceIn(0, _tiles.size), cell) } else { - // Add the tile with a temporary row which will get reassigned when + // Add the tile with a temporary row/col which will get reassigned when // regenerating spacers - _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0)) + _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0, 0)) } regenerateGrid() diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt index e749475479d8..d55763aaeddb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt @@ -19,7 +19,7 @@ package com.android.systemui.qs.panels.ui.compose import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons @@ -97,7 +97,9 @@ constructor( TileGrid(tiles = page, modifier = Modifier, editModeStart = {}) } } - Box(modifier = Modifier.height(FooterHeight).fillMaxWidth()) { + // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is + // expected to be inside a scrollable container, this should not be an issue. + Box(modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth()) { PagerDots( pagerState = pagerState, activeColor = MaterialTheme.colorScheme.primary, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt index a645b51404e7..1c7a334d3ef2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt @@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.util.fastMap @@ -29,6 +31,7 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.grid.ui.compose.VerticalSpannedGrid import com.android.systemui.qs.composefragment.ui.GridAnchor import com.android.systemui.qs.panels.ui.compose.infinitegrid.Tile +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey import com.android.systemui.res.R @@ -38,10 +41,11 @@ fun SceneScope.QuickQuickSettings( viewModel: QuickQuickSettingsViewModel, modifier: Modifier = Modifier, ) { - val sizedTiles by - viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList()) + val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle() val tiles = sizedTiles.fastMap { it.tile } + val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } } val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle() + val scope = rememberCoroutineScope() DisposableEffect(tiles) { val token = Any() @@ -49,6 +53,7 @@ fun SceneScope.QuickQuickSettings( onDispose { tiles.forEach { it.stopListening(token) } } } val columns by viewModel.columns.collectAsStateWithLifecycle() + var cellIndex = 0 Box(modifier = modifier) { GridAnchor() VerticalSpannedGrid( @@ -59,11 +64,15 @@ fun SceneScope.QuickQuickSettings( modifier = Modifier.sysuiResTag("qqs_tile_layout"), ) { spanIndex -> val it = sizedTiles[spanIndex] + val column = cellIndex % columns + cellIndex += it.width Tile( tile = it.tile, iconOnly = it.isIcon, modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)), squishiness = { squishiness }, + coroutineScope = scope, + bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt index 9ec5a82a18d7..71fa0ac30fb7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt @@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -54,6 +55,7 @@ import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.toggleableState +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.android.compose.modifiers.background import com.android.compose.modifiers.thenIf @@ -64,7 +66,6 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState import com.android.systemui.res.R -import kotlinx.coroutines.delay private const val TEST_TAG_TOGGLE = "qs_tile_toggle_target" @@ -138,13 +139,20 @@ fun LargeTileLabels( accessibilityUiState: AccessibilityUiState? = null, ) { Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) { - Text(label, color = colors.label, modifier = Modifier.tileMarquee()) + Text( + label, + style = MaterialTheme.typography.labelLarge, + color = colors.label, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) if (!TextUtils.isEmpty(secondaryLabel)) { Text( secondaryLabel ?: "", color = colors.secondaryLabel, + style = MaterialTheme.typography.bodyMedium, modifier = - Modifier.tileMarquee().thenIf( + Modifier.thenIf( accessibilityUiState?.stateDescription?.contains(secondaryLabel ?: "") == true ) { @@ -182,10 +190,7 @@ fun SmallTileContent( rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true) } else { var atEnd by remember(icon.res) { mutableStateOf(false) } - LaunchedEffect(key1 = icon.res) { - delay(350) - atEnd = true - } + LaunchedEffect(key1 = icon.res) { atEnd = true } rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt index 45c1e48840ee..5c2a2bd2b78c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt @@ -22,13 +22,13 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.animateIntAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.LocalOverscrollConfiguration import androidx.compose.foundation.background import androidx.compose.foundation.border +import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -46,7 +46,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyGridItemScope import androidx.compose.foundation.lazy.grid.LazyGridScope import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.rememberScrollState @@ -66,19 +65,17 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.BiasAlignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.positionInRoot @@ -98,23 +95,26 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastMap -import androidx.compose.ui.zIndex +import com.android.compose.animation.bounceable import com.android.compose.modifiers.background import com.android.compose.modifiers.height import com.android.systemui.common.ui.compose.load import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.SizedTileImpl +import com.android.systemui.qs.panels.ui.compose.BounceableInfo import com.android.systemui.qs.panels.ui.compose.DragAndDropState import com.android.systemui.qs.panels.ui.compose.EditTileListState +import com.android.systemui.qs.panels.ui.compose.bounceableInfo import com.android.systemui.qs.panels.ui.compose.dragAndDropRemoveZone import com.android.systemui.qs.panels.ui.compose.dragAndDropTileList import com.android.systemui.qs.panels.ui.compose.dragAndDropTileSource import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileArrangementPadding +import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.ToggleTargetSize import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState -import com.android.systemui.qs.panels.ui.compose.selection.ResizingHandle +import com.android.systemui.qs.panels.ui.compose.selection.ResizableTileContainer import com.android.systemui.qs.panels.ui.compose.selection.TileWidths import com.android.systemui.qs.panels.ui.compose.selection.clearSelectionTile import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState @@ -122,11 +122,14 @@ import com.android.systemui.qs.panels.ui.compose.selection.selectableTile import com.android.systemui.qs.panels.ui.model.GridCell import com.android.systemui.qs.panels.ui.model.SpacerGridCell import com.android.systemui.qs.panels.ui.model.TileGridCell +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.shared.model.groupAndSort import com.android.systemui.res.R +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay +import kotlinx.coroutines.launch object TileType @@ -241,15 +244,20 @@ private fun CurrentTilesGrid( onSetTiles: (List<TileSpec>) -> Unit, ) { val currentListState by rememberUpdatedState(listState) - val tileHeight = CommonTileDefaults.TileHeight val totalRows = listState.tiles.lastOrNull()?.row ?: 0 val totalHeight by animateDpAsState( - gridHeight(totalRows + 1, tileHeight, TileArrangementPadding, CurrentTilesGridPadding), + gridHeight(totalRows + 1, TileHeight, TileArrangementPadding, CurrentTilesGridPadding), label = "QSEditCurrentTilesGridHeight", ) val gridState = rememberLazyGridState() var gridContentOffset by remember { mutableStateOf(Offset(0f, 0f)) } + val coroutineScope = rememberCoroutineScope() + + val cells = + remember(listState.tiles) { + listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) } + } TileLazyGrid( state = gridState, @@ -272,7 +280,7 @@ private fun CurrentTilesGrid( } .testTag(CURRENT_TILES_GRID_TEST_TAG), ) { - EditTiles(listState.tiles, listState, selectionState) { spec -> + EditTiles(cells, columns, listState, selectionState, coroutineScope) { spec -> // Toggle the current size of the tile currentListState.isIcon(spec)?.let { onResize(spec, !it) } } @@ -286,10 +294,10 @@ private fun AvailableTileGrid( columns: Int, dragAndDropState: DragAndDropState, ) { - // Available tiles aren't visible during drag and drop, so the row isn't needed + // Available tiles aren't visible during drag and drop, so the row/col isn't needed val groupedTiles = remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) { - groupAndSort(tiles.fastMap { TileGridCell(it, 0) }) + groupAndSort(tiles.fastMap { TileGridCell(it, 0, 0) }) } val labelColors = EditModeTileDefaults.editTileColors() @@ -349,24 +357,26 @@ private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any { /** * Adds a list of [GridCell] to the lazy grid * - * @param cells the list of [GridCell] + * @param cells the pairs of [GridCell] to [BounceableTileViewModel] * @param dragAndDropState the [DragAndDropState] for this grid * @param selectionState the [MutableSelectionState] for this grid * @param onToggleSize the callback when a tile's size is toggled */ fun LazyGridScope.EditTiles( - cells: List<GridCell>, + cells: List<Pair<GridCell, BounceableTileViewModel>>, + columns: Int, dragAndDropState: DragAndDropState, selectionState: MutableSelectionState, + coroutineScope: CoroutineScope, onToggleSize: (spec: TileSpec) -> Unit, ) { items( count = cells.size, - key = { cells[it].key(it, dragAndDropState) }, - span = { cells[it].span }, + key = { cells[it].first.key(it, dragAndDropState) }, + span = { cells[it].first.span }, contentType = { TileType }, ) { index -> - when (val cell = cells[index]) { + when (val cell = cells[index].first) { is TileGridCell -> if (dragAndDropState.isMoving(cell.tile.tileSpec)) { // If the tile is being moved, replace it with a visible spacer @@ -385,6 +395,9 @@ fun LazyGridScope.EditTiles( dragAndDropState = dragAndDropState, selectionState = selectionState, onToggleSize = onToggleSize, + coroutineScope = coroutineScope, + bounceableInfo = cells.bounceableInfo(index, columns), + modifier = Modifier.animateItem(), ) } is SpacerGridCell -> SpacerGridCell() @@ -393,12 +406,15 @@ fun LazyGridScope.EditTiles( } @Composable -private fun LazyGridItemScope.TileGridCell( +private fun TileGridCell( cell: TileGridCell, index: Int, dragAndDropState: DragAndDropState, selectionState: MutableSelectionState, onToggleSize: (spec: TileSpec) -> Unit, + coroutineScope: CoroutineScope, + bounceableInfo: BounceableInfo, + modifier: Modifier = Modifier, ) { val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1) var selected by remember { mutableStateOf(false) } @@ -407,6 +423,9 @@ private fun LazyGridItemScope.TileGridCell( targetValue = if (selected) 1f else 0f, label = "QSEditTileSelectionAlpha", ) + val selectionColor = MaterialTheme.colorScheme.primary + val colors = EditModeTileDefaults.editTileColors() + val currentBounceableInfo by rememberUpdatedState(bounceableInfo) LaunchedEffect(selectionState.selection?.tileSpec) { selectionState.selection?.let { @@ -420,152 +439,61 @@ private fun LazyGridItemScope.TileGridCell( selected = selectionState.selection?.tileSpec == cell.tile.tileSpec } - val modifier = - Modifier.animateItem() - .semantics(mergeDescendants = true) { - this.stateDescription = stateDescription - contentDescription = cell.tile.label.text - customActions = - listOf( - // TODO(b/367748260): Add final accessibility actions - CustomAccessibilityAction("Toggle size") { - onToggleSize(cell.tile.tileSpec) - true - } - ) - } - .height(CommonTileDefaults.TileHeight) - .fillMaxWidth() - - val content = - @Composable { - EditTile( - tileViewModel = cell.tile, - iconOnly = cell.isIcon, - selectionAlpha = { selectionAlpha }, - modifier = - Modifier.fillMaxSize() - .selectableTile(cell.tile.tileSpec, selectionState) - .dragAndDropTileSource( - SizedTileImpl(cell.tile, cell.width), - dragAndDropState, - selectionState::unSelect, - ), - ) - } - - if (selected) { - SelectedTile( - isIcon = cell.isIcon, - selectionAlpha = { selectionAlpha }, - selectionState = selectionState, - modifier = modifier.zIndex(2f), // 2f to display this tile over neighbors when dragged - content = content, - ) - } else { - UnselectedTile( - selectionAlpha = { selectionAlpha }, - selectionState = selectionState, - modifier = modifier, - content = content, - ) - } -} - -@Composable -private fun SelectedTile( - isIcon: Boolean, - selectionAlpha: () -> Float, - selectionState: MutableSelectionState, - modifier: Modifier = Modifier, - content: @Composable () -> Unit, -) { // Current base, min and max width of this tile var tileWidths: TileWidths? by remember { mutableStateOf(null) } - - // Animated diff between the current width and the resized width of the tile. We can't use - // animateContentSize here as the tile is sometimes unbounded. - val remainingOffset by - animateIntAsState( - selectionState.resizingState?.let { tileWidths?.base?.minus(it.width) ?: 0 } ?: 0, - label = "QSEditTileWidthOffset", - ) - val padding = with(LocalDensity.current) { TileArrangementPadding.roundToPx() } - Box( - modifier.onSizeChanged { - val min = if (isIcon) it.width else (it.width - padding) / 2 - val max = if (isIcon) (it.width * 2) + padding else it.width - tileWidths = TileWidths(it.width, min, max) - } + + ResizableTileContainer( + selected = selected, + selectionState = selectionState, + selectionAlpha = { selectionAlpha }, + selectionColor = selectionColor, + tileWidths = { tileWidths }, + modifier = + modifier + .height(TileHeight) + .fillMaxWidth() + .onSizeChanged { + // Grab the size before the bounceable to get the idle width + val min = if (cell.isIcon) it.width else (it.width - padding) / 2 + val max = if (cell.isIcon) (it.width * 2) + padding else it.width + tileWidths = TileWidths(it.width, min, max) + } + .bounceable( + bounceable = currentBounceableInfo.bounceable, + previousBounceable = currentBounceableInfo.previousTile, + nextBounceable = currentBounceableInfo.nextTile, + orientation = Orientation.Horizontal, + bounceEnd = currentBounceableInfo.bounceEnd, + ), ) { - val handle = - @Composable { - ResizingHandle( - enabled = true, - selectionState = selectionState, - transition = selectionAlpha, - tileWidths = { tileWidths }, + Box( + modifier + .fillMaxSize() + .semantics(mergeDescendants = true) { + this.stateDescription = stateDescription + contentDescription = cell.tile.label.text + customActions = + listOf( + // TODO(b/367748260): Add final accessibility actions + CustomAccessibilityAction("Toggle size") { + onToggleSize(cell.tile.tileSpec) + true + } + ) + } + .selectableTile(cell.tile.tileSpec, selectionState) { + coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } + } + .dragAndDropTileSource( + SizedTileImpl(cell.tile, cell.width), + dragAndDropState, + selectionState::unSelect, ) - } - - Layout(contents = listOf(content, handle)) { - (contentMeasurables, handleMeasurables), - constraints -> - // Grab the width from the resizing state if a resize is in progress, otherwise fill the - // max width - val width = - selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset) - val contentPlaceable = - contentMeasurables.first().measure(constraints.copy(maxWidth = width)) - val handlePlaceable = handleMeasurables.first().measure(constraints) - - // Place the dot vertically centered on the right edge - val handleX = contentPlaceable.width - (handlePlaceable.width / 2) - val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2) - - layout(constraints.maxWidth, constraints.maxHeight) { - contentPlaceable.place(0, 0) - handlePlaceable.place(handleX, handleY) - } - } - } -} - -@Composable -private fun UnselectedTile( - selectionAlpha: () -> Float, - selectionState: MutableSelectionState, - modifier: Modifier = Modifier, - content: @Composable () -> Unit, -) { - val handle = - @Composable { - ResizingHandle( - enabled = false, - selectionState = selectionState, - transition = selectionAlpha, - ) - } - - Box(modifier) { - Layout(contents = listOf(content, handle)) { - (contentMeasurables, handleMeasurables), - constraints -> - val contentPlaceable = - contentMeasurables - .first() - .measure(constraints.copy(maxWidth = constraints.maxWidth)) - val handlePlaceable = handleMeasurables.first().measure(constraints) - - // Place the dot vertically centered on the right edge - val handleX = contentPlaceable.width - (handlePlaceable.width / 2) - val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2) - - layout(constraints.maxWidth, constraints.maxHeight) { - contentPlaceable.place(0, 0) - handlePlaceable.place(handleX, handleY) - } + .tileBackground(colors.background) + .tilePadding() + ) { + EditTile(tile = cell.tile, iconOnly = cell.isIcon) } } } @@ -588,19 +516,19 @@ private fun AvailableTileGridCell( verticalArrangement = spacedBy(CommonTileDefaults.TilePadding, Alignment.Top), modifier = modifier, ) { - EditTileContainer( - colors = colors, - modifier = - Modifier.fillMaxWidth() - .height(CommonTileDefaults.TileHeight) - .clearSelectionTile(selectionState) - .semantics(mergeDescendants = true) { - onClick(onClickActionName) { false } - this.stateDescription = stateDescription - } - .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) { - selectionState.unSelect() - }, + Box( + Modifier.fillMaxWidth() + .height(TileHeight) + .clearSelectionTile(selectionState) + .semantics(mergeDescendants = true) { + onClick(onClickActionName) { false } + this.stateDescription = stateDescription + } + .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) { + selectionState.unSelect() + } + .tileBackground(colors.background) + .tilePadding() ) { // Icon SmallTileContent( @@ -626,16 +554,14 @@ private fun AvailableTileGridCell( @Composable private fun SpacerGridCell(modifier: Modifier = Modifier) { // By default, spacers are invisible and exist purely to catch drag movements - Box(modifier.height(CommonTileDefaults.TileHeight).fillMaxWidth()) + Box(modifier.height(TileHeight).fillMaxWidth()) } @Composable -fun EditTile( - tileViewModel: EditTileViewModel, +fun BoxScope.EditTile( + tile: EditTileViewModel, iconOnly: Boolean, - modifier: Modifier = Modifier, colors: TileColors = EditModeTileDefaults.editTileColors(), - selectionAlpha: () -> Float = { 1f }, ) { // Animated horizontal alignment from center (0f) to start (-1f) val alignmentValue by @@ -646,68 +572,36 @@ fun EditTile( val alignment by remember { derivedStateOf { BiasAlignment(horizontalBias = alignmentValue, verticalBias = 0f) } } + // Icon + Box(Modifier.size(ToggleTargetSize).align(alignment)) { + SmallTileContent( + icon = tile.icon, + color = colors.icon, + animateToEnd = true, + modifier = Modifier.align(Alignment.Center), + ) + } - EditTileContainer(colors = colors, selectionAlpha = selectionAlpha, modifier = modifier) { - // Icon - Box(Modifier.size(ToggleTargetSize).align(alignment)) { - SmallTileContent( - icon = tileViewModel.icon, - color = colors.icon, - animateToEnd = true, - modifier = Modifier.align(Alignment.Center), - ) - } - - // Labels, positioned after the icon - AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) { - LargeTileLabels( - label = tileViewModel.label.text, - secondaryLabel = tileViewModel.appName?.text, - colors = colors, - modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding), - ) - } + // Labels, positioned after the icon + AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) { + LargeTileLabels( + label = tile.label.text, + secondaryLabel = tile.appName?.text, + colors = colors, + modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding), + ) } } -@Composable -private fun EditTileContainer( - colors: TileColors, - modifier: Modifier = Modifier, - selectionAlpha: () -> Float = { 0f }, - selectionColor: Color = MaterialTheme.colorScheme.primary, - content: @Composable BoxScope.() -> Unit = {}, -) { - Box( - Modifier.wrapContentSize().drawWithContent { - drawContent() - drawRoundRect( - SolidColor(selectionColor), - cornerRadius = CornerRadius(InactiveCornerRadius.toPx()), - style = Stroke(EditModeTileDefaults.SelectedBorderWidth.toPx()), - alpha = selectionAlpha(), - ) - } - ) { - Box( - modifier = - modifier - .drawBehind { - drawRoundRect( - SolidColor(colors.background), - cornerRadius = CornerRadius(InactiveCornerRadius.toPx()), - ) - } - .tilePadding(), - content = content, - ) +private fun Modifier.tileBackground(color: Color): Modifier { + return drawBehind { + drawRoundRect(SolidColor(color), cornerRadius = CornerRadius(InactiveCornerRadius.toPx())) } } private object EditModeTileDefaults { const val PLACEHOLDER_ALPHA = .3f val EditGridHeaderHeight = 60.dp - val SelectedBorderWidth = 2.dp val CurrentTilesGridPadding = 8.dp @Composable diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt index 6920e498bdde..e5c213519415 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.util.fastMap @@ -29,7 +30,9 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.grid.ui.compose.VerticalSpannedGrid import com.android.systemui.qs.panels.shared.model.SizedTileImpl import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout +import com.android.systemui.qs.panels.ui.compose.bounceableInfo import com.android.systemui.qs.panels.ui.compose.rememberEditListState +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsViewModel @@ -62,7 +65,11 @@ constructor( } val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle() val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) } + val bounceables = + remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } } val squishiness by squishinessViewModel.squishiness.collectAsStateWithLifecycle() + val scope = rememberCoroutineScope() + var cellIndex = 0 VerticalSpannedGrid( columns = columns, @@ -71,11 +78,15 @@ constructor( spans = sizedTiles.fastMap { it.width }, ) { spanIndex -> val it = sizedTiles[spanIndex] + val column = cellIndex % columns + cellIndex += it.width Tile( tile = it.tile, iconOnly = iconTilesViewModel.isIconTile(it.tile.spec), modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)), squishiness = { squishiness }, + coroutineScope = scope, + bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt index 4bd5b2d68c4c..52d526123430 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt @@ -23,8 +23,8 @@ import android.service.quicksettings.Tile.STATE_ACTIVE import android.service.quicksettings.Tile.STATE_INACTIVE import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box @@ -44,6 +44,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -61,11 +62,13 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.Expandable +import com.android.compose.animation.bounceable import com.android.compose.modifiers.thenIf import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.panels.ui.compose.BounceableInfo import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel import com.android.systemui.qs.panels.ui.viewmodel.TileUiState @@ -74,6 +77,8 @@ import com.android.systemui.qs.panels.ui.viewmodel.toUiState import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.res.R import java.util.function.Supplier +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch private const val TEST_TAG_SMALL = "qs_tile_small" private const val TEST_TAG_LARGE = "qs_tile_large" @@ -102,9 +107,12 @@ fun Tile( tile: TileViewModel, iconOnly: Boolean, squishiness: () -> Float, + coroutineScope: CoroutineScope, + bounceableInfo: BounceableInfo, modifier: Modifier = Modifier, ) { val state by tile.state.collectAsStateWithLifecycle(tile.currentState) + val currentBounceableInfo by rememberUpdatedState(bounceableInfo) val resources = resources() val uiState = remember(state, resources) { state.toUiState(resources) } val colors = TileDefaults.getColorForState(uiState) @@ -112,7 +120,7 @@ fun Tile( // TODO(b/361789146): Draw the shapes instead of clipping val tileShape = TileDefaults.animateTileShape(uiState.state) - TileContainer( + TileExpandable( color = if (iconOnly || !uiState.handlesSecondaryClick) { colors.iconBackground @@ -120,93 +128,101 @@ fun Tile( colors.background }, shape = tileShape, - iconOnly = iconOnly, - onClick = tile::onClick, - onLongClick = tile::onLongClick, - uiState = uiState, squishiness = squishiness, - modifier = modifier, + modifier = + modifier + .fillMaxWidth() + .bounceable( + bounceable = currentBounceableInfo.bounceable, + previousBounceable = currentBounceableInfo.previousTile, + nextBounceable = currentBounceableInfo.nextTile, + orientation = Orientation.Horizontal, + bounceEnd = currentBounceableInfo.bounceEnd, + ), ) { expandable -> - val icon = getTileIcon(icon = uiState.icon) - if (iconOnly) { - SmallTileContent( - icon = icon, - color = colors.icon, - modifier = Modifier.align(Alignment.Center), - ) - } else { - val iconShape = TileDefaults.animateIconShape(uiState.state) - LargeTileContent( - label = uiState.label, - secondaryLabel = uiState.secondaryLabel, - icon = icon, - colors = colors, - iconShape = iconShape, - toggleClickSupported = state.handlesSecondaryClick, - onClick = { - if (state.handlesSecondaryClick) { - tile.onSecondaryClick() - } - }, - onLongClick = { tile.onLongClick(expandable) }, - accessibilityUiState = uiState.accessibilityUiState, - squishiness = squishiness, - ) + TileContainer( + onClick = { + tile.onClick(expandable) + if (uiState.accessibilityUiState.toggleableState != null) { + coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } + } + }, + onLongClick = { tile.onLongClick(expandable) }, + uiState = uiState, + iconOnly = iconOnly, + ) { + val icon = getTileIcon(icon = uiState.icon) + if (iconOnly) { + SmallTileContent( + icon = icon, + color = colors.icon, + modifier = Modifier.align(Alignment.Center), + ) + } else { + val iconShape = TileDefaults.animateIconShape(uiState.state) + LargeTileContent( + label = uiState.label, + secondaryLabel = uiState.secondaryLabel, + icon = icon, + colors = colors, + iconShape = iconShape, + toggleClickSupported = state.handlesSecondaryClick, + onClick = { + if (state.handlesSecondaryClick) { + tile.onSecondaryClick() + } + }, + onLongClick = { tile.onLongClick(expandable) }, + accessibilityUiState = uiState.accessibilityUiState, + squishiness = squishiness, + ) + } } } } @Composable -private fun TileContainer( +private fun TileExpandable( color: Color, shape: Shape, - iconOnly: Boolean, - uiState: TileUiState, squishiness: () -> Float, modifier: Modifier = Modifier, - onClick: (Expandable) -> Unit = {}, - onLongClick: (Expandable) -> Unit = {}, - content: @Composable BoxScope.(Expandable) -> Unit, + content: @Composable (Expandable) -> Unit, ) { Expandable( color = color, shape = shape, modifier = modifier.clip(shape).verticalSquish(squishiness), ) { - val longPressLabel = longPressLabel() - Box( - modifier = - Modifier.height(CommonTileDefaults.TileHeight) - .fillMaxWidth() - .combinedClickable( - onClick = { onClick(it) }, - onLongClick = { onLongClick(it) }, - onClickLabel = uiState.accessibilityUiState.clickLabel, - onLongClickLabel = longPressLabel, - ) - .semantics { - role = uiState.accessibilityUiState.accessibilityRole - if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) { - uiState.accessibilityUiState.toggleableState?.let { - toggleableState = it - } - } - stateDescription = uiState.accessibilityUiState.stateDescription - } - .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE) - .thenIf(iconOnly) { - Modifier.semantics { - contentDescription = uiState.accessibilityUiState.contentDescription - } - } - .tilePadding() - ) { - content(it) - } + content(it) } } @Composable +fun TileContainer( + onClick: () -> Unit, + onLongClick: () -> Unit, + uiState: TileUiState, + iconOnly: Boolean, + content: @Composable BoxScope.() -> Unit, +) { + Box( + modifier = + Modifier.height(CommonTileDefaults.TileHeight) + .fillMaxWidth() + .tileCombinedClickable( + onClick = onClick, + onLongClick = onLongClick, + uiState = uiState, + iconOnly = iconOnly, + ) + .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE) + .tilePadding(), + content = content, + ) +} + +@Composable private fun getTileIcon(icon: Supplier<QSTile.Icon?>): Icon { val context = LocalContext.current return icon.get()?.let { @@ -222,14 +238,38 @@ fun tileHorizontalArrangement(): Arrangement.Horizontal { return spacedBy(space = CommonTileDefaults.TileArrangementPadding, alignment = Alignment.Start) } -fun Modifier.tileMarquee(): Modifier { - return basicMarquee(iterations = 1, initialDelayMillis = 200) -} - fun Modifier.tilePadding(): Modifier { return padding(CommonTileDefaults.TilePadding) } +@Composable +fun Modifier.tileCombinedClickable( + onClick: () -> Unit, + onLongClick: () -> Unit, + uiState: TileUiState, + iconOnly: Boolean, +): Modifier { + val longPressLabel = longPressLabel() + return combinedClickable( + onClick = onClick, + onLongClick = onLongClick, + onClickLabel = uiState.accessibilityUiState.clickLabel, + onLongClickLabel = longPressLabel, + ) + .semantics { + role = uiState.accessibilityUiState.accessibilityRole + if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) { + uiState.accessibilityUiState.toggleableState?.let { toggleableState = it } + } + stateDescription = uiState.accessibilityUiState.stateDescription + } + .thenIf(iconOnly) { + Modifier.semantics { + contentDescription = uiState.accessibilityUiState.contentDescription + } + } +} + data class TileColors( val background: Color, val iconBackground: Color, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt index 441d96289d86..1d36aee4eb85 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt @@ -89,8 +89,11 @@ class MutableSelectionState( * Listens for click events to select/unselect the given [TileSpec]. Use this on current tiles as * they can be selected. */ -@Composable -fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelectionState): Modifier { +fun Modifier.selectableTile( + tileSpec: TileSpec, + selectionState: MutableSelectionState, + onClick: () -> Unit = {}, +): Modifier { return pointerInput(Unit) { detectTapGestures( onTap = { @@ -99,6 +102,7 @@ fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelection } else { selectionState.select(tileSpec, manual = true) } + onClick() } ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt index e0f0b6aa8919..9f13a3788f53 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt @@ -16,63 +16,117 @@ package com.android.systemui.qs.panels.ui.compose.selection +import androidx.compose.animation.core.animateIntAsState import androidx.compose.foundation.Canvas import androidx.compose.foundation.gestures.detectHorizontalDragGestures import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.size import androidx.compose.foundation.systemGestureExclusion import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.layout +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.toSize +import androidx.compose.ui.zIndex +import com.android.compose.modifiers.thenIf +import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingDotSize +import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth /** - * Dot handling resizing drag events. Use this on the selected tile to resize it + * Places a dot to handle resizing drag events. Use this on tiles to resize. * - * @param enabled whether resizing drag events should be handled + * The dot is placed vertically centered on the right border. The [content] will have a border when + * selected. + * + * @param selected whether resizing drag events should be handled * @param selectionState the [MutableSelectionState] on the grid - * @param transition the animated value for the dot, used for its alpha and scale + * @param selectionAlpha the animated value for the dot and border alpha + * @param selectionColor the [Color] of the dot and border * @param tileWidths the [TileWidths] of the selected tile - * @param onResize the callback when the drag passes the resizing threshold */ @Composable -fun ResizingHandle( +fun ResizableTileContainer( + selected: Boolean, + selectionState: MutableSelectionState, + selectionAlpha: () -> Float, + selectionColor: Color, + tileWidths: () -> TileWidths?, + modifier: Modifier = Modifier, + content: @Composable BoxScope.() -> Unit = {}, +) { + Box( + modifier + .resizable(selected, selectionState, tileWidths) + .selectionBorder(selectionColor, selectionAlpha) + ) { + content() + ResizingHandle( + enabled = selected, + selectionState = selectionState, + transition = selectionAlpha, + tileWidths = tileWidths, + modifier = + // Higher zIndex to make sure the handle is drawn above the content + Modifier.zIndex(2f), + ) + } +} + +@Composable +private fun ResizingHandle( enabled: Boolean, selectionState: MutableSelectionState, transition: () -> Float, - tileWidths: () -> TileWidths? = { null }, + tileWidths: () -> TileWidths?, + modifier: Modifier = Modifier, ) { - if (enabled) { - // Manually creating the touch target around the resizing dot to ensure that the next tile - // does - // not receive the touch input accidentally. - val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current - Box( - Modifier.size(minTouchTargetSize) - .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } - .pointerInput(Unit) { - detectHorizontalDragGestures( - onHorizontalDrag = { _, offset -> selectionState.onResizingDrag(offset) }, - onDragStart = { - tileWidths()?.let { selectionState.onResizingDragStart(it) } - }, - onDragEnd = selectionState::onResizingDragEnd, - onDragCancel = selectionState::onResizingDragEnd, + // Manually creating the touch target around the resizing dot to ensure that the next tile + // does not receive the touch input accidentally. + val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current + Box( + modifier + .layout { measurable, constraints -> + val size = minTouchTargetSize.roundToPx() + val placeable = measurable.measure(Constraints(size, size, size, size)) + layout(placeable.width, placeable.height) { + placeable.place( + x = constraints.maxWidth - placeable.width / 2, + y = constraints.maxHeight / 2 - placeable.height / 2, ) } - ) { - ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center)) - } - } else { - ResizingDot(transition = transition) + } + .thenIf(enabled) { + Modifier.systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } + .pointerInput(Unit) { + detectHorizontalDragGestures( + onHorizontalDrag = { _, offset -> + selectionState.onResizingDrag(offset) + }, + onDragStart = { + tileWidths()?.let { selectionState.onResizingDragStart(it) } + }, + onDragEnd = selectionState::onResizingDragEnd, + onDragCancel = selectionState::onResizingDragEnd, + ) + } + } + ) { + ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center)) } } @@ -88,6 +142,45 @@ private fun ResizingDot( } } +private fun Modifier.selectionBorder( + selectionColor: Color, + selectionAlpha: () -> Float = { 0f }, +): Modifier { + return drawWithContent { + drawContent() + drawRoundRect( + SolidColor(selectionColor), + cornerRadius = CornerRadius(InactiveCornerRadius.toPx()), + style = Stroke(SelectedBorderWidth.toPx()), + alpha = selectionAlpha(), + ) + } +} + +@Composable +private fun Modifier.resizable( + selected: Boolean, + selectionState: MutableSelectionState, + tileWidths: () -> TileWidths?, +): Modifier { + if (!selected) return zIndex(1f) + + // Animated diff between the current width and the resized width of the tile. We can't use + // animateContentSize here as the tile is sometimes unbounded. + val remainingOffset by + animateIntAsState( + selectionState.resizingState?.let { tileWidths()?.base?.minus(it.width) ?: 0 } ?: 0, + label = "QSEditTileWidthOffset", + ) + return zIndex(2f).layout { measurable, constraints -> + // Grab the width from the resizing state if a resize is in progress + val width = selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset) + val placeable = measurable.measure(constraints.copy(minWidth = width, maxWidth = width)) + layout(constraints.maxWidth, placeable.height) { placeable.place(0, 0) } + } +} + private object SelectionDefaults { val ResizingDotSize = 16.dp + val SelectedBorderWidth = 2.dp } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt index b1841c4c5ffa..c0441f8a38a1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt @@ -31,8 +31,8 @@ sealed interface GridCell { } /** - * Represents a [EditTileViewModel] from a grid associated with a tile format and the row it's - * positioned at + * Represents a [EditTileViewModel] from a grid associated with a tile format and the row and column + * it's positioned at */ @Immutable data class TileGridCell( @@ -41,13 +41,15 @@ data class TileGridCell( override val width: Int, override val span: GridItemSpan = GridItemSpan(width), override val s: String = "${tile.tileSpec.spec}-$row-$width", + val column: Int, ) : GridCell, SizedTile<EditTileViewModel>, CategoryAndName by tile { val key: String = "${tile.tileSpec.spec}-$row" constructor( sizedTile: SizedTile<EditTileViewModel>, row: Int, - ) : this(tile = sizedTile.tile, row = row, width = sizedTile.width) + column: Int, + ) : this(tile = sizedTile.tile, row = row, column = column, width = sizedTile.width) } /** Represents an empty space used to fill incomplete rows. Will always display as a 1x1 tile */ @@ -73,7 +75,13 @@ fun List<SizedTile<EditTileViewModel>>.toGridCells( return splitInRowsSequence(this, columns) .flatMapIndexed { rowIndex, sizedTiles -> val correctedRowIndex = rowIndex + startingRow - val row: List<GridCell> = sizedTiles.map { TileGridCell(it, correctedRowIndex) } + var column = 0 + val row: List<GridCell> = + sizedTiles.map { + TileGridCell(it, correctedRowIndex, column).also { cell -> + column += cell.width + } + } // Fill the incomplete rows with spacers val numSpacers = columns - sizedTiles.sumOf { it.width } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt new file mode 100644 index 000000000000..506b05256880 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.ui.viewmodel + +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.VectorConverter +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.android.compose.animation.Bounceable + +class BounceableTileViewModel : Bounceable { + private val animatableBounce = Animatable(0.dp, Dp.VectorConverter) + override val bounce: Dp + get() = animatableBounce.value + + suspend fun animateBounce() { + animatableBounce.animateTo(BounceSize) + animatableBounce.animateTo(0.dp) + } + + private companion object { + val BounceSize = 8.dp + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt index ee12736f6db4..be6ce5c5b4f4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt @@ -43,13 +43,13 @@ data class UnloadedEditTileViewModel( ) { fun load(context: Context): EditTileViewModel { return EditTileViewModel( - tileSpec, - icon, - label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec), - appName?.toAnnotatedString(context), - isCurrent, - availableEditActions, - category, + tileSpec = tileSpec, + icon = icon, + label = label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec), + appName = appName?.toAnnotatedString(context), + isCurrent = isCurrent, + availableEditActions = availableEditActions, + category = category, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt index b8f4ab40bb1d..dde36289f139 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt @@ -16,7 +16,7 @@ package com.android.systemui.qs.tiles.base.viewmodel -import com.android.app.tracing.coroutines.createCoroutineTracingContext +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -28,5 +28,7 @@ class QSTileCoroutineScopeFactory constructor(@Application private val applicationScope: CoroutineScope) { fun create(): CoroutineScope = - CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("QSTileScope")) + CoroutineScope( + applicationScope.coroutineContext + SupervisorJob() + newTracingContext("QSTileScope") + ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt index ae56c2aad4e9..f6749715e1fd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt @@ -15,12 +15,12 @@ */ package com.android.systemui.qs.tiles.dialog -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.util.Log import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.animation.Expandable +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.phone.SystemUIDialog @@ -63,7 +63,7 @@ constructor( } return } else { - coroutineScope = CoroutineScope(bgDispatcher + createCoroutineTracingContext("InternetDialogScope")) + coroutineScope = CoroutineScope(bgDispatcher + newTracingContext("InternetDialogScope")) dialog = dialogFactory .create(aboveStatusBar, canConfigMobileData, canConfigWifi, coroutineScope) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt new file mode 100644 index 000000000000..8dd611f9911a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.hearingdevices.domain + +import android.content.res.Resources +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import javax.inject.Inject + +/** Maps [HearingDevicesTileModel] to [QSTileState]. */ +class HearingDevicesTileMapper +@Inject +constructor(@Main private val resources: Resources, private val theme: Resources.Theme) : + QSTileDataToStateMapper<HearingDevicesTileModel> { + + override fun map(config: QSTileConfig, data: HearingDevicesTileModel): QSTileState = + QSTileState.build(resources, theme, config.uiConfig) { + label = resources.getString(R.string.quick_settings_hearing_devices_label) + iconRes = R.drawable.qs_hearing_devices_icon + val loadedIcon = + Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null) + icon = { loadedIcon } + sideViewIcon = QSTileState.SideViewIcon.Chevron + contentDescription = label + if (data.isAnyActiveHearingDevice) { + activationState = QSTileState.ActivationState.ACTIVE + secondaryLabel = + resources.getString(R.string.quick_settings_hearing_devices_connected) + } else if (data.isAnyPairedHearingDevice) { + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = + resources.getString(R.string.quick_settings_hearing_devices_disconnected) + } else { + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = "" + } + supportedActions = + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt new file mode 100644 index 000000000000..ec0a4e9db896 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor + +import android.os.UserHandle +import com.android.systemui.Flags +import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.android.systemui.statusbar.policy.BluetoothController +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn + +/** Observes hearing devices state changes providing the [HearingDevicesTileModel]. */ +class HearingDevicesTileDataInteractor +@Inject +constructor( + @Background private val backgroundContext: CoroutineContext, + private val bluetoothController: BluetoothController, + private val hearingDevicesChecker: HearingDevicesChecker, +) : QSTileDataInteractor<HearingDevicesTileModel> { + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger>, + ): Flow<HearingDevicesTileModel> = + conflatedCallbackFlow { + val callback = + object : BluetoothController.Callback { + override fun onBluetoothStateChange(enabled: Boolean) { + trySend(getModel()) + } + + override fun onBluetoothDevicesChanged() { + trySend(getModel()) + } + } + bluetoothController.addCallback(callback) + awaitClose { bluetoothController.removeCallback(callback) } + } + .flowOn(backgroundContext) + .distinctUntilChanged() + + override fun availability(user: UserHandle): Flow<Boolean> = + flowOf(Flags.hearingAidsQsTileDialog()) + + private fun getModel() = + HearingDevicesTileModel( + hearingDevicesChecker.isAnyActiveHearingDevice, + hearingDevicesChecker.isAnyPairedHearingDevice, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt new file mode 100644 index 000000000000..5e7172ee3ba7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor + +import android.content.Intent +import android.provider.Settings +import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager +import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.withContext + +/** Handles hearing devices tile clicks. */ +class HearingDevicesTileUserActionInteractor +@Inject +constructor( + @Main private val mainContext: CoroutineContext, + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, + private val hearingDevicesDialogManager: HearingDevicesDialogManager, +) : QSTileUserActionInteractor<HearingDevicesTileModel> { + + override suspend fun handleInput(input: QSTileInput<HearingDevicesTileModel>) = + with(input) { + when (action) { + is QSTileUserAction.Click -> { + withContext(mainContext) { + hearingDevicesDialogManager.showDialog( + action.expandable, + LAUNCH_SOURCE_QS_TILE, + ) + } + } + is QSTileUserAction.LongClick -> { + qsTileIntentUserActionHandler.handle( + action.expandable, + Intent(Settings.ACTION_HEARING_DEVICES_SETTINGS), + ) + } + is QSTileUserAction.ToggleClick -> {} + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt new file mode 100644 index 000000000000..4e37b771c49b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.hearingdevices.domain.model + +/** Hearing devices tile model */ +data class HearingDevicesTileModel( + val isAnyActiveHearingDevice: Boolean, + val isAnyPairedHearingDevice: Boolean, +) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt index ac75932b8fee..14115444fe49 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt @@ -16,9 +16,9 @@ package com.android.systemui.qs.tiles.impl.location.domain.interactor -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.content.Intent import android.provider.Settings +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.plugins.ActivityStarter @@ -53,9 +53,11 @@ constructor( val wasEnabled: Boolean = input.data.isEnabled if (keyguardController.isMethodSecure() && keyguardController.isShowing()) { activityStarter.postQSRunnableDismissingKeyguard { - CoroutineScope(applicationScope.coroutineContext + createCoroutineTracingContext("LocationTileScope")).launch { - locationController.setLocationEnabled(!wasEnabled) - } + CoroutineScope( + applicationScope.coroutineContext + + newTracingContext("LocationTileScope") + ) + .launch { locationController.setLocationEnabled(!wasEnabled) } } } else { withContext(coroutineContext) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt index 40591bf56e0a..cc14e71986f5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.qs.tiles.impl.modes.domain.interactor import android.content.Context import android.os.UserHandle -import com.android.app.tracing.coroutines.flow.map +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.qualifiers.Background @@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map class ModesTileDataInteractor @Inject @@ -59,6 +60,7 @@ constructor( fun tileData() = zenModeInteractor.activeModes .map { activeModes -> buildTileData(activeModes) } + .flowName("tileData") .flowOn(bgDispatcher) .distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt index 801a0ce4b744..537b56bccae8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt @@ -39,7 +39,7 @@ constructor(@Main private val resources: Resources, val theme: Resources.Theme) val loadedIcon: Icon.Loaded = when (val dataIcon = data.icon) { is Icon.Resource -> { - if (iconRes != dataIcon.res) { + if (data.iconResId != dataIcon.res) { Log.wtf( "ModesTileMapper", "Icon.Resource.res & iconResId are not identical", diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt index 8e80fb0b4c3e..33dc6ed7a1e8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.rotation.ui.mapper import android.content.res.Resources +import android.hardware.devicestate.DeviceStateManager import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper @@ -25,6 +26,7 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileState import com.android.systemui.res.R import com.android.systemui.statusbar.policy.DevicePostureController +import com.android.systemui.util.Utils.isDeviceFoldable import javax.inject.Inject /** Maps [RotationLockTileModel] to [QSTileState]. */ @@ -33,7 +35,8 @@ class RotationLockTileMapper constructor( @Main private val resources: Resources, private val theme: Resources.Theme, - private val devicePostureController: DevicePostureController + private val devicePostureController: DevicePostureController, + private val deviceStateManager: DeviceStateManager ) : QSTileDataToStateMapper<RotationLockTileModel> { override fun map(config: QSTileConfig, data: RotationLockTileModel): QSTileState = QSTileState.build(resources, theme, config.uiConfig) { @@ -58,7 +61,7 @@ constructor( this.icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null) } - if (isDeviceFoldable()) { + if (isDeviceFoldable(resources, deviceStateManager)) { this.secondaryLabel = getSecondaryLabelWithPosture(this.activationState) } this.stateDescription = this.secondaryLabel @@ -67,11 +70,6 @@ constructor( setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK) } - private fun isDeviceFoldable(): Boolean { - val intArray = resources.getIntArray(com.android.internal.R.array.config_foldedDeviceStates) - return intArray.isNotEmpty() - } - private fun getSecondaryLabelWithPosture(activationState: QSTileState.ActivationState): String { val stateNames = resources.getStringArray(R.array.tile_states_rotation) val stateName = diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt index 468e180a6e41..f03c7521931c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt @@ -16,12 +16,12 @@ package com.android.systemui.qs.tiles.impl.saver.domain -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.content.Context import android.content.DialogInterface import android.content.SharedPreferences import android.os.Bundle import com.android.internal.R +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.DataSaverController @@ -45,9 +45,8 @@ class DataSaverDialogDelegate( setTitle(R.string.data_saver_enable_title) setMessage(R.string.data_saver_description) setPositiveButton(R.string.data_saver_enable_button) { _: DialogInterface?, _ -> - CoroutineScope(backgroundContext + createCoroutineTracingContext("DataSaverDialogScope")).launch { - dataSaverController.setDataSaverEnabled(true) - } + CoroutineScope(backgroundContext + newTracingContext("DataSaverDialogScope")) + .launch { dataSaverController.setDataSaverEnabled(true) } sharedPreferences .edit() diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java index f02b871b196d..a32bf1ec70ae 100644 --- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java @@ -16,6 +16,8 @@ package com.android.systemui.reardisplay; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -26,6 +28,7 @@ import android.content.res.Resources; import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateManagerGlobal; +import android.hardware.devicestate.feature.flags.Flags; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup.LayoutParams; @@ -42,6 +45,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.airbnb.lottie.LottieAnimationView; import com.airbnb.lottie.LottieDrawable; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Executor; import javax.inject.Inject; @@ -66,11 +71,12 @@ public class RearDisplayDialogController implements ConfigurationController.ConfigurationListener, CommandQueue.Callbacks { - private int[] mFoldedStates; + private List<Integer> mFoldedStates; private boolean mStartedFolded; private boolean mServiceNotified = false; private int mAnimationRepeatCount = LottieDrawable.INFINITE; + private final DeviceStateManager mDeviceStateManager; private DeviceStateManagerGlobal mDeviceStateManagerGlobal; private DeviceStateManager.DeviceStateCallback mDeviceStateManagerCallback = new DeviceStateManagerCallback(); @@ -90,12 +96,14 @@ public class RearDisplayDialogController implements @Main Executor executor, @Main Resources resources, LayoutInflater layoutInflater, - SystemUIDialog.Factory systemUIDialogFactory) { + SystemUIDialog.Factory systemUIDialogFactory, + DeviceStateManager deviceStateManager) { mCommandQueue = commandQueue; mExecutor = executor; mResources = resources; mLayoutInflater = layoutInflater; mSystemUIDialogFactory = systemUIDialogFactory; + mDeviceStateManager = deviceStateManager; } @Override @@ -180,10 +188,23 @@ public class RearDisplayDialogController implements */ private void initializeValues(int startingBaseState) { mRearDisplayEducationDialog = mSystemUIDialogFactory.create(); - // TODO(b/329170810): Refactor and remove with updated DeviceStateManager values. - if (mFoldedStates == null) { - mFoldedStates = mResources.getIntArray( - com.android.internal.R.array.config_foldedDeviceStates); + if (Flags.deviceStatePropertyMigration()) { + if (mFoldedStates == null) { + mFoldedStates = new ArrayList<>(); + List<DeviceState> deviceStates = mDeviceStateManager.getSupportedDeviceStates(); + for (int i = 0; i < deviceStates.size(); i++) { + DeviceState state = deviceStates.get(i); + if (state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)) { + mFoldedStates.add(state.getIdentifier()); + } + } + } + } else { + // TODO(b/329170810): Refactor and remove with updated DeviceStateManager values. + if (mFoldedStates == null) { + mFoldedStates = copyIntArrayToList(mResources.getIntArray( + com.android.internal.R.array.config_foldedDeviceStates)); + } } mStartedFolded = isFoldedState(startingBaseState); mDeviceStateManagerGlobal = DeviceStateManagerGlobal.getInstance(); @@ -191,9 +212,17 @@ public class RearDisplayDialogController implements mExecutor); } + private List<Integer> copyIntArrayToList(int[] intArray) { + List<Integer> integerList = new ArrayList<>(intArray.length); + for (int i = 0; i < intArray.length; i++) { + integerList.add(intArray[i]); + } + return integerList; + } + private boolean isFoldedState(int state) { - for (int i = 0; i < mFoldedStates.length; i++) { - if (mFoldedStates[i] == state) return true; + for (int i = 0; i < mFoldedStates.size(); i++) { + if (mFoldedStates.get(i) == state) return true; } return false; } @@ -213,7 +242,7 @@ public class RearDisplayDialogController implements * TestAPI to allow us to set the folded states array, instead of reading from resources. */ @TestApi - void setFoldedStates(int[] foldedStates) { + void setFoldedStates(List<Integer> foldedStates) { mFoldedStates = foldedStates; } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt index 7b6b0f614cc2..6097ef53f8df 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt @@ -20,7 +20,6 @@ package com.android.systemui.scene.shared.flag import com.android.systemui.Flags.FLAG_SCENE_CONTAINER import com.android.systemui.Flags.sceneContainer -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils import com.android.systemui.keyguard.KeyguardBottomAreaRefactor @@ -42,8 +41,7 @@ object SceneContainerFlag { KeyguardWmStateRefactor.isEnabled && MigrateClocksToBlueprint.isEnabled && NotificationThrottleHun.isEnabled && - PredictiveBackSysUiFlag.isEnabled && - DeviceEntryUdfpsRefactor.isEnabled + PredictiveBackSysUiFlag.isEnabled // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer @@ -58,7 +56,6 @@ object SceneContainerFlag { MigrateClocksToBlueprint.token, NotificationThrottleHun.token, PredictiveBackSysUiFlag.token, - DeviceEntryUdfpsRefactor.token, // NOTE: Changes should also be made in isEnabled and @EnableSceneContainer ) diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index ce942fefa393..47254775618c 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.scene.ui.viewmodel import android.view.MotionEvent import android.view.View import androidx.compose.runtime.getValue -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.DefaultEdgeDetector import com.android.compose.animation.scene.ObservableTransitionState diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt index 54e0319da58f..17b1b6d91229 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt @@ -26,7 +26,7 @@ import android.os.UserHandle import android.util.Log import android.util.Pair import android.view.Window -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.app.ChooserActivity import com.android.systemui.dagger.qualifiers.Application import dagger.assisted.Assisted diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt index 9e622801a01b..7b01c36489fb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt @@ -31,7 +31,7 @@ import android.view.RemoteAnimationAdapter import android.view.RemoteAnimationTarget import android.view.WindowManager import android.view.WindowManagerGlobal -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.infra.ServiceConnector import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java index acfcd13738f0..2259b55dc268 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java @@ -313,7 +313,9 @@ public class LegacyScreenshotController implements InteractiveScreenshotHandler setWindowFocusable(true); mViewProxy.requestFocus(); - enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle()); + if (screenshot.getType() != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) { + enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle()); + } attachWindow(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt index f5c605211520..08214c456897 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt @@ -35,6 +35,7 @@ import android.util.Log import android.view.Display import android.view.ScrollCaptureResponse import android.view.ViewRootImpl.ActivityConfigCallback +import android.view.WindowManager import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE import android.widget.Toast import android.window.WindowContext @@ -217,7 +218,9 @@ internal constructor( window.setFocusable(true) viewProxy.requestFocus() - enqueueScrollCaptureRequest(requestId, screenshot.userHandle) + if (screenshot.type != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) { + enqueueScrollCaptureRequest(requestId, screenshot.userHandle) + } window.attachWindow() diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt index c8067df114fc..6df22f036273 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt @@ -21,7 +21,7 @@ import android.os.RemoteException import android.util.Log import androidx.lifecycle.LifecycleService import androidx.lifecycle.lifecycleScope -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.shade.ShadeExpansionStateManager diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt index d3a7fc4a3e4a..7aeec47241cb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt @@ -18,7 +18,7 @@ package com.android.systemui.screenshot import android.media.MediaPlayer import android.util.Log -import com.android.app.tracing.coroutines.async +import com.android.app.tracing.coroutines.asyncTraced as async import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 757e37e9341f..750f5ad62bab 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -32,7 +32,6 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.classifier.Classifier.UNLOCK; import static com.android.systemui.keyguard.shared.model.KeyguardState.AOD; import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING; -import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED; import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE; import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN; import static com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED; @@ -123,7 +122,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.doze.DozeLog; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpsysTableLogger; @@ -144,7 +142,6 @@ import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; -import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel; @@ -605,9 +602,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel; private final LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel; private final GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel; - private final GoneToDreamingLockscreenHostedTransitionViewModel - mGoneToDreamingLockscreenHostedTransitionViewModel; - private final LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel; private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; private final SharedNotificationContainerInteractor mSharedNotificationContainerInteractor; @@ -619,7 +613,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final CoroutineDispatcher mMainDispatcher; private boolean mIsAnyMultiShadeExpanded; private boolean mIsOcclusionTransitionRunning = false; - private boolean mIsGoneToDreamingLockscreenHostedTransitionRunning; private int mDreamingToLockscreenTransitionTranslationY; private int mLockscreenToDreamingTransitionTranslationY; private int mGoneToDreamingTransitionTranslationY; @@ -663,24 +656,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump step.getTransitionState() == TransitionState.RUNNING; }; - private final Consumer<TransitionStep> mGoneToDreamingLockscreenHostedTransition = - (TransitionStep step) -> { - mIsOcclusionTransitionRunning = - step.getTransitionState() == TransitionState.RUNNING; - mIsGoneToDreamingLockscreenHostedTransitionRunning = mIsOcclusionTransitionRunning; - }; - - private final Consumer<TransitionStep> mLockscreenToDreamingLockscreenHostedTransition = - (TransitionStep step) -> { - mIsOcclusionTransitionRunning = - step.getTransitionState() == TransitionState.RUNNING; - }; - - private final Consumer<TransitionStep> mDreamingLockscreenHostedToLockscreenTransition = - (TransitionStep step) -> { - mIsOcclusionTransitionRunning = - step.getTransitionState() == TransitionState.RUNNING; - }; private final Consumer<TransitionStep> mLockscreenToOccludedTransition = (TransitionStep step) -> { @@ -765,8 +740,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel, LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel, GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel, - GoneToDreamingLockscreenHostedTransitionViewModel - goneToDreamingLockscreenHostedTransitionViewModel, LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel, PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, @Main CoroutineDispatcher mainDispatcher, @@ -805,8 +778,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel; mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel; mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel; - mGoneToDreamingLockscreenHostedTransitionViewModel = - goneToDreamingLockscreenHostedTransitionViewModel; mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel; mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel; mKeyguardTransitionInteractor = keyguardTransitionInteractor; @@ -1134,25 +1105,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mDreamingToLockscreenTransitionTranslationY), setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); - // Gone -> Dreaming hosted in lockscreen - collectFlow(mView, mKeyguardTransitionInteractor - .transition(Edge.Companion.create(Scenes.Gone, DREAMING_LOCKSCREEN_HOSTED), - Edge.Companion.create(GONE, DREAMING_LOCKSCREEN_HOSTED)), - mGoneToDreamingLockscreenHostedTransition, mMainDispatcher); - collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - - // Lockscreen -> Dreaming hosted in lockscreen - collectFlow(mView, mKeyguardTransitionInteractor - .transition(Edge.Companion.create(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)), - mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher); - - // Dreaming hosted in lockscreen -> Lockscreen - collectFlow(mView, mKeyguardTransitionInteractor - .transition(Edge.Companion.create(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)), - mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher); - // Occluded->Lockscreen collectFlow(mView, mKeyguardTransitionInteractor.transition( Edge.Companion.create(OCCLUDED, LOCKSCREEN)), @@ -1646,11 +1598,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mAnimateNextPositionUpdate = false; } - private boolean shouldAnimateKeyguardStatusViewAlignment() { - // Do not animate when transitioning from Gone->DreamingLockscreenHosted - return !mIsGoneToDreamingLockscreenHostedTransitionRunning; - } - private void updateClockAppearance() { int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard; boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); @@ -1661,7 +1608,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusViewController.displayClock(computeDesiredClockSize(), shouldAnimateClockChange); } - updateKeyguardStatusViewAlignment(/* animate= */shouldAnimateKeyguardStatusViewAlignment()); + updateKeyguardStatusViewAlignment(/* animate= */true); int userSwitcherHeight = mKeyguardQsUserSwitchController != null ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0; if (mKeyguardUserSwitcherController != null) { @@ -1808,10 +1755,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // overlap. return true; } - if (isActiveDreamLockscreenHosted()) { - // Dreaming hosted in lockscreen, no "visible" notifications. Safe to center the clock. - return true; - } if (mNotificationListContainer.hasPulsingNotifications()) { // Pulsing notification appears on the right. Move clock left to avoid overlap. return false; @@ -1841,10 +1784,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } - private boolean isActiveDreamLockscreenHosted() { - return mKeyguardInteractor.isActiveDreamLockscreenHosted().getValue(); - } - private boolean hasVisibleNotifications() { if (FooterViewRefactor.isEnabled()) { return mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue() @@ -1859,16 +1798,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump /** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */ private float getLockIconPadding() { float lockIconPadding = 0f; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView() - .findViewById(R.id.device_entry_icon_view); - if (deviceEntryIconView != null) { - lockIconPadding = mNotificationStackScrollLayoutController.getBottom() - - deviceEntryIconView.getTop(); - } - } else if (mLockIconViewController.getTop() != 0f) { + View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView() + .findViewById(R.id.device_entry_icon_view); + if (deviceEntryIconView != null) { lockIconPadding = mNotificationStackScrollLayoutController.getBottom() - - mLockIconViewController.getTop(); + - deviceEntryIconView.getTop(); } return lockIconPadding; } @@ -5059,8 +4993,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump return false; } - if (DeviceEntryUdfpsRefactor.isEnabled() - && mAlternateBouncerInteractor.isVisibleState()) { + if (mAlternateBouncerInteractor.isVisibleState()) { // never send touches to shade if the alternate bouncer is showing return false; } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index cae141d014a8..36449bee8bf6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -16,7 +16,6 @@ package com.android.systemui.shade; -import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING; import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; @@ -35,7 +34,6 @@ import androidx.core.view.ViewKt; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.KeyguardUnfoldTransition; -import com.android.keyguard.LockIconViewController; import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -44,7 +42,6 @@ import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags; import com.android.systemui.bouncer.ui.binder.BouncerViewBinder; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dock.DockManager; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; @@ -75,7 +72,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeScrimController; import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; @@ -101,14 +97,11 @@ public class NotificationShadeWindowViewController implements Dumpable { private final NotificationShadeDepthController mDepthController; private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; - private final LockIconViewController mLockIconViewController; private final ShadeLogger mShadeLogger; - private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final StatusBarWindowStateController mStatusBarWindowStateController; private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private final AmbientState mAmbientState; private final PulsingGestureListener mPulsingGestureListener; - private final LockscreenHostedDreamGestureListener mLockscreenHostedDreamGestureListener; private final NotificationInsetsController mNotificationInsetsController; private final FeatureFlagsClassic mFeatureFlagsClassic; private final SysUIKeyEventHandler mSysUIKeyEventHandler; @@ -119,7 +112,6 @@ public class NotificationShadeWindowViewController implements Dumpable { private final GlanceableHubContainerController mGlanceableHubContainerController; private GestureDetector mPulsingWakeupGestureHandler; - private GestureDetector mDreamingWakeupGestureHandler; private View mBrightnessMirror; private boolean mTouchActive; private boolean mTouchCancelled; @@ -174,9 +166,7 @@ public class NotificationShadeWindowViewController implements Dumpable { PanelExpansionInteractor panelExpansionInteractor, ShadeExpansionStateManager shadeExpansionStateManager, NotificationStackScrollLayoutController notificationStackScrollLayoutController, - StatusBarKeyguardViewManager statusBarKeyguardViewManager, StatusBarWindowStateController statusBarWindowStateController, - LockIconViewController lockIconViewController, CentralSurfaces centralSurfaces, DozeServiceHost dozeServiceHost, DozeScrimController dozeScrimController, @@ -189,7 +179,6 @@ public class NotificationShadeWindowViewController implements Dumpable { ShadeLogger shadeLogger, DumpManager dumpManager, PulsingGestureListener pulsingGestureListener, - LockscreenHostedDreamGestureListener lockscreenHostedDreamGestureListener, KeyguardTransitionInteractor keyguardTransitionInteractor, GlanceableHubContainerController glanceableHubContainerController, NotificationLaunchAnimationInteractor notificationLaunchAnimationInteractor, @@ -210,9 +199,7 @@ public class NotificationShadeWindowViewController implements Dumpable { mShadeExpansionStateManager = shadeExpansionStateManager; mDepthController = depthController; mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; - mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mStatusBarWindowStateController = statusBarWindowStateController; - mLockIconViewController = lockIconViewController; mShadeLogger = shadeLogger; mService = centralSurfaces; mDozeServiceHost = dozeServiceHost; @@ -221,7 +208,6 @@ public class NotificationShadeWindowViewController implements Dumpable { mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; mAmbientState = ambientState; mPulsingGestureListener = pulsingGestureListener; - mLockscreenHostedDreamGestureListener = lockscreenHostedDreamGestureListener; mNotificationInsetsController = notificationInsetsController; mKeyguardTransitionInteractor = keyguardTransitionInteractor; mGlanceableHubContainerController = glanceableHubContainerController; @@ -259,7 +245,6 @@ public class NotificationShadeWindowViewController implements Dumpable { mDisableSubpixelTextTransitionListener)); } - lockIconViewController.setLockIconView(mView.findViewById(R.id.lock_icon_view)); dumpManager.registerDumpable(this); } @@ -337,10 +322,6 @@ public class NotificationShadeWindowViewController implements Dumpable { mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller); mPulsingWakeupGestureHandler = new GestureDetector(mView.getContext(), mPulsingGestureListener); - if (mFeatureFlagsClassic.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { - mDreamingWakeupGestureHandler = new GestureDetector(mView.getContext(), - mLockscreenHostedDreamGestureListener); - } mView.setLayoutInsetsController(mNotificationInsetsController); mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() { boolean mUseDragDownHelperForTouch = false; @@ -392,9 +373,6 @@ public class NotificationShadeWindowViewController implements Dumpable { } if (mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) { - // If the user was sliding their finger across the lock screen, - // we may have been intercepting the touch and forwarding it to the - // UDFPS affordance via mStatusBarKeyguardViewManager.onTouch (see below). // If this touch ended up unlocking the device, we want to cancel the touch // immediately, so we don't cause swipe or expand animations afterwards. cancelCurrentTouch(); @@ -415,13 +393,6 @@ public class NotificationShadeWindowViewController implements Dumpable { // GlanceableHubContainerController is only used pre-flexiglass. return logDownDispatch(ev, "dispatched to glanceable hub container", true); } - if (mDreamingWakeupGestureHandler != null - && mDreamingWakeupGestureHandler.onTouchEvent(ev)) { - return logDownDispatch(ev, "dream wakeup gesture handled", true); - } - if (mStatusBarKeyguardViewManager.dispatchTouchEvent(ev)) { - return logDownDispatch(ev, "dispatched to Keyguard", true); - } if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == View.VISIBLE) { // Disallow new pointers while the brightness mirror is visible. This is so that @@ -512,7 +483,6 @@ public class NotificationShadeWindowViewController implements Dumpable { if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing() && !mDockManager.isDocked() - && !mLockIconViewController.willHandleTouchWhileDozing(ev) ) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mShadeLogger.d("NSWVC: capture all touch events in always-on"); @@ -520,22 +490,8 @@ public class NotificationShadeWindowViewController implements Dumpable { return true; } - if (mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(ev)) { - // Don't allow touches to proceed to underlying views if alternate - // bouncer is showing - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mShadeLogger.d("NSWVC: alt bouncer showing"); - } - return true; - } - - boolean bouncerShowing; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing() + boolean bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing() || mAlternateBouncerInteractor.isVisibleState(); - } else { - bouncerShowing = mService.isBouncerShowing(); - } if (mPanelExpansionInteractor.isFullyExpanded() && !bouncerShowing && !mStatusBarStateController.isDozing()) { @@ -603,9 +559,6 @@ public class NotificationShadeWindowViewController implements Dumpable { if (mStatusBarStateController.isDozing()) { handled = !mDozeServiceHost.isPulsing(); } - if (mStatusBarKeyguardViewManager.onTouch(ev)) { - return true; - } if (MigrateClocksToBlueprint.isEnabled()) { if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) { // we still want to finish our drag down gesture when locking the screen diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt index 949d2aa36bf3..460bfbbcb3ab 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade.domain.interactor +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardRepository @@ -62,17 +63,20 @@ constructor( override val isShadeEnabled: StateFlow<Boolean> = disableFlagsRepository.disableFlags .map { it.isShadeEnabled() } + .flowName("isShadeEnabled") .stateIn(scope, SharingStarted.Eagerly, initialValue = false) override val isQsEnabled: StateFlow<Boolean> = disableFlagsRepository.disableFlags .map { it.isQuickSettingsEnabled() } + .flowName("isQsEnabled") .stateIn(scope, SharingStarted.Eagerly, initialValue = false) override val isAnyFullyExpanded: StateFlow<Boolean> = anyExpansion .map { it >= 1f } .distinctUntilChanged() + .flowName("isAnyFullyExpanded") .stateIn(scope, SharingStarted.Eagerly, initialValue = false) override val isShadeFullyExpanded: Flow<Boolean> = @@ -81,6 +85,7 @@ constructor( override val isShadeAnyExpanded: StateFlow<Boolean> = baseShadeInteractor.shadeExpansion .map { it > 0 } + .flowName("isShadeAnyExpanded") .stateIn(scope, SharingStarted.Eagerly, false) override val isShadeFullyCollapsed: Flow<Boolean> = @@ -88,6 +93,7 @@ constructor( override val isUserInteracting: StateFlow<Boolean> = combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs } + .flowName("isUserInteracting") .stateIn(scope, SharingStarted.Eagerly, false) override val isShadeTouchable: Flow<Boolean> = diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt index 3113dc462e6a..4bdd36773655 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt @@ -16,7 +16,6 @@ package com.android.systemui.shade.ui.viewmodel -import com.android.app.tracing.coroutines.flow.map import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction @@ -33,6 +32,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map /** * Models the UI state for the user actions that the user can perform to navigate to other scenes. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 769abafed69f..f99d8f140670 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -32,7 +32,6 @@ import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; import static com.android.systemui.DejankUtils.whitelistIpcs; -import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_IS_DISMISSIBLE; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ADAPTIVE_AUTH; @@ -226,18 +225,8 @@ public class KeyguardIndicationController { private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; private boolean mDozing; - private boolean mIsActiveDreamLockscreenHosted; private final ScreenLifecycle mScreenLifecycle; @VisibleForTesting - final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback = - (Boolean isLockscreenHosted) -> { - if (mIsActiveDreamLockscreenHosted == isLockscreenHosted) { - return; - } - mIsActiveDreamLockscreenHosted = isLockscreenHosted; - updateDeviceEntryIndication(false); - }; - @VisibleForTesting final Consumer<Set<Integer>> mCoExAcquisitionMsgIdsToShowCallback = (Set<Integer> coExFaceAcquisitionMsgIdsToShow) -> mCoExFaceAcquisitionMsgIdsToShow = coExFaceAcquisitionMsgIdsToShow; @@ -423,10 +412,6 @@ public class KeyguardIndicationController { intentFilter.addAction(Intent.ACTION_USER_REMOVED); mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, intentFilter); } - if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { - collectFlow(mIndicationArea, mKeyguardInteractor.isActiveDreamLockscreenHosted(), - mIsActiveDreamLockscreenHostedCallback); - } collectFlow(mIndicationArea, mBiometricMessageInteractor.getCoExFaceAcquisitionMsgIdsToShow(), @@ -1032,12 +1017,6 @@ public class KeyguardIndicationController { return; } - // Device is dreaming and the dream is hosted in lockscreen - if (mIsActiveDreamLockscreenHosted) { - mIndicationArea.setVisibility(GONE); - return; - } - // A few places might need to hide the indication, so always start by making it visible mIndicationArea.setVisibility(VISIBLE); @@ -1247,7 +1226,6 @@ public class KeyguardIndicationController { pw.println(" mBiometricMessageFollowUp: " + mBiometricMessageFollowUp); pw.println(" mBatteryLevel: " + mBatteryLevel); pw.println(" mBatteryPresent: " + mBatteryPresent); - pw.println(" mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted); pw.println(" AOD text: " + ( mTopIndicationView == null ? null : mTopIndicationView.getText())); pw.println(" computePowerIndication(): " + computePowerIndication()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 1481b734ff61..1ec5357d0d30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -15,7 +15,6 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.ExpandHelper import com.android.systemui.Gefingerpoken -import com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy import com.android.systemui.classifier.Classifier import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton @@ -90,6 +89,7 @@ constructor( @get:VisibleForTesting var fractionToShade: Float = 0f private set + private var useSplitShade: Boolean = false private lateinit var nsslController: NotificationStackScrollLayoutController lateinit var centralSurfaces: CentralSurfaces @@ -161,9 +161,6 @@ constructor( val distanceUntilShowingPulsingNotifications get() = fullTransitionDistance - /** The udfpsKeyguardViewController if it exists. */ - var mUdfpsKeyguardViewControllerLegacy: UdfpsKeyguardViewControllerLegacy? = null - /** The touch helper responsible for the drag down animation. */ val touchHelper = DragDownHelper( @@ -171,7 +168,7 @@ constructor( this, naturalScrollingSettingObserver, shadeRepository, - context + context, ) private val splitShadeOverScroller: SplitShadeLockScreenOverScroller by lazy { @@ -448,7 +445,6 @@ constructor( val udfpsProgress = MathUtils.saturate(dragDownAmount / udfpsTransitionDistance) shadeRepository.setUdfpsTransitionToFullShadeProgress(udfpsProgress) - mUdfpsKeyguardViewControllerLegacy?.setTransitionToFullShadeProgress(udfpsProgress) val statusBarProgress = MathUtils.saturate(dragDownAmount / statusBarTransitionDistance) centralSurfaces.setTransitionToFullShadeProgress(statusBarProgress) @@ -457,7 +453,7 @@ constructor( private fun setDragDownAmountAnimated( target: Float, delay: Long = 0, - endlistener: (() -> Unit)? = null + endlistener: (() -> Unit)? = null, ) { logger.logDragDownAnimation(target) val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target) @@ -553,7 +549,7 @@ constructor( private fun goToLockedShadeInternal( expandView: View?, animationHandler: ((Long) -> Unit)? = null, - cancelAction: Runnable? = null + cancelAction: Runnable? = null, ) { if (!shadeInteractor.isShadeEnabled.value) { cancelAction?.run() @@ -564,10 +560,7 @@ constructor( var entry: NotificationEntry? = null if (expandView is ExpandableNotificationRow) { entry = expandView.entry - entry.setUserExpanded( - /* userExpanded= */ true, - /* allowChildExpansion= */ true, - ) + entry.setUserExpanded(/* userExpanded= */ true, /* allowChildExpansion= */ true) // Indicate that the group expansion is changing at this time -- this way the group // and children backgrounds / divider animations will look correct. entry.setGroupExpansionChanging(true) @@ -594,9 +587,7 @@ constructor( statusBarStateController.setLeaveOpenOnKeyguardHide(false) draggedDownEntry?.apply { setUserLocked(false) - notifyHeightChanged( - /* needsAnimation= */ false, - ) + notifyHeightChanged(/* needsAnimation= */ false) draggedDownEntry = null } cancelAction?.run() @@ -614,9 +605,7 @@ constructor( // This call needs to be after updating the shade state since otherwise // the scrimstate resets too early if (animationHandler != null) { - animationHandler.invoke( - /* delay= */ 0, - ) + animationHandler.invoke(/* delay= */ 0) } else { performDefaultGoToFullShadeAnimation(0) } @@ -757,7 +746,7 @@ class DragDownHelper( private val dragDownCallback: LockscreenShadeTransitionController, private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver, private val shadeRepository: ShadeRepository, - context: Context + context: Context, ) : Gefingerpoken { private var dragDownAmountOnStart = 0.0f @@ -932,7 +921,7 @@ class DragDownHelper( @VisibleForTesting fun cancelChildExpansion( child: ExpandableView, - animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS + animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS, ) { if (child.actualHeight == child.collapsedHeight) { expandCallback.setUserLockedChild(child, false) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 5c45f3d1bbc8..1db7fb429629 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -32,14 +32,15 @@ import androidx.annotation.VisibleForTesting import androidx.dynamicanimation.animation.FloatPropertyCompat import androidx.dynamicanimation.animation.SpringAnimation import androidx.dynamicanimation.animation.SpringForce -import com.android.systemui.Dumpable import com.android.app.animation.Interpolators +import com.android.systemui.Dumpable import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionListener +import com.android.systemui.shared.Flags.ambientAod import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK import com.android.systemui.statusbar.phone.DozeParameters @@ -54,10 +55,13 @@ import kotlin.math.max import kotlin.math.sign /** - * Controller responsible for statusbar window blur. + * Responsible for blurring the notification shade window, and applying a zoom effect to the + * wallpaper. */ @SysUISingleton -class NotificationShadeDepthController @Inject constructor( +class NotificationShadeDepthController +@Inject +constructor( private val statusBarStateController: StatusBarStateController, private val blurUtils: BlurUtils, private val biometricUnlockController: BiometricUnlockController, @@ -69,7 +73,7 @@ class NotificationShadeDepthController @Inject constructor( private val context: Context, private val splitShadeStateController: SplitShadeStateController, dumpManager: DumpManager, - configurationController: ConfigurationController + configurationController: ConfigurationController, ) : ShadeExpansionListener, Dumpable { companion object { private const val WAKE_UP_ANIMATION_ENABLED = true @@ -85,8 +89,7 @@ class NotificationShadeDepthController @Inject constructor( private var keyguardAnimator: Animator? = null private var notificationAnimator: Animator? = null private var updateScheduled: Boolean = false - @VisibleForTesting - var shadeExpansion = 0f + @VisibleForTesting var shadeExpansion = 0f private var isClosed: Boolean = true private var isOpen: Boolean = false private var isBlurred: Boolean = false @@ -106,13 +109,13 @@ class NotificationShadeDepthController @Inject constructor( var shadeAnimation = DepthAnimation() - @VisibleForTesting - var brightnessMirrorSpring = DepthAnimation() + @VisibleForTesting var brightnessMirrorSpring = DepthAnimation() var brightnessMirrorVisible: Boolean = false set(value) { field = value - brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f).toInt() - else 0) + brightnessMirrorSpring.animateTo( + if (value) blurUtils.blurRadiusOfRatio(1f).toInt() else 0 + ) } var qsPanelExpansion = 0f @@ -126,9 +129,7 @@ class NotificationShadeDepthController @Inject constructor( scheduleUpdate() } - /** - * How much we're transitioning to the full shade - */ + /** How much we're transitioning to the full shade */ var transitionToFullShadeProgress = 0f set(value) { if (field == value) return @@ -160,19 +161,15 @@ class NotificationShadeDepthController @Inject constructor( shadeAnimation.finishIfRunning() } - /** - * We're unlocking, and should not blur as the panel expansion changes. - */ + /** We're unlocking, and should not blur as the panel expansion changes. */ var blursDisabledForUnlock: Boolean = false - set(value) { - if (field == value) return - field = value - scheduleUpdate() - } + set(value) { + if (field == value) return + field = value + scheduleUpdate() + } - /** - * Force stop blur effect when necessary. - */ + /** Force stop blur effect when necessary. */ private var scrimsVisible: Boolean = false set(value) { if (field == value) return @@ -180,9 +177,7 @@ class NotificationShadeDepthController @Inject constructor( scheduleUpdate() } - /** - * Blur radius of the wake-up animation on this frame. - */ + /** Blur radius of the wake-up animation on this frame. */ private var wakeAndUnlockBlurRadius = 0f set(value) { if (field == value) return @@ -191,15 +186,23 @@ class NotificationShadeDepthController @Inject constructor( } private fun computeBlurAndZoomOut(): Pair<Int, Float> { - val animationRadius = MathUtils.constrain(shadeAnimation.radius, - blurUtils.minBlurRadius.toFloat(), blurUtils.maxBlurRadius.toFloat()) - val expansionRadius = blurUtils.blurRadiusOfRatio( + val animationRadius = + MathUtils.constrain( + shadeAnimation.radius, + blurUtils.minBlurRadius.toFloat(), + blurUtils.maxBlurRadius.toFloat(), + ) + val expansionRadius = + blurUtils.blurRadiusOfRatio( ShadeInterpolation.getNotificationScrimAlpha( - if (shouldApplyShadeBlur()) shadeExpansion else 0f)) - var combinedBlur = (expansionRadius * INTERACTION_BLUR_FRACTION + + if (shouldApplyShadeBlur()) shadeExpansion else 0f + ) + ) + var combinedBlur = + (expansionRadius * INTERACTION_BLUR_FRACTION + animationRadius * ANIMATION_BLUR_FRACTION) - val qsExpandedRatio = ShadeInterpolation.getNotificationScrimAlpha(qsPanelExpansion) * - shadeExpansion + val qsExpandedRatio = + ShadeInterpolation.getNotificationScrimAlpha(qsPanelExpansion) * shadeExpansion combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsExpandedRatio)) combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress)) var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius) @@ -231,83 +234,97 @@ class NotificationShadeDepthController @Inject constructor( return Pair(blur, zoomOut) } - /** - * Callback that updates the window blur value and is called only once per frame. - */ + /** Callback that updates the window blur value and is called only once per frame. */ @VisibleForTesting - val updateBlurCallback = Choreographer.FrameCallback { - updateScheduled = false - val (blur, zoomOut) = computeBlurAndZoomOut() - val opaque = scrimsVisible && !blursDisabledForAppLaunch - Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur) - blurUtils.applyBlur(root.viewRootImpl, blur, opaque) - lastAppliedBlur = blur - wallpaperController.setNotificationShadeZoom(zoomOut) - listeners.forEach { - it.onWallpaperZoomOutChanged(zoomOut) - it.onBlurRadiusChanged(blur) - } - notificationShadeWindowController.setBackgroundBlurRadius(blur) - } - - /** - * Animate blurs when unlocking. - */ - private val keyguardStateCallback = object : KeyguardStateController.Callback { - override fun onKeyguardFadingAwayChanged() { - if (!keyguardStateController.isKeyguardFadingAway || - biometricUnlockController.mode != MODE_WAKE_AND_UNLOCK) { - return + val updateBlurCallback = + Choreographer.FrameCallback { + updateScheduled = false + val (blur, zoomOut) = computeBlurAndZoomOut() + val opaque = scrimsVisible && !blursDisabledForAppLaunch + Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur) + blurUtils.applyBlur(root.viewRootImpl, blur, opaque) + lastAppliedBlur = blur + wallpaperController.setNotificationShadeZoom(zoomOut) + listeners.forEach { + it.onWallpaperZoomOutChanged(zoomOut) + it.onBlurRadiusChanged(blur) } + notificationShadeWindowController.setBackgroundBlurRadius(blur) + } - keyguardAnimator?.cancel() - keyguardAnimator = ValueAnimator.ofFloat(1f, 0f).apply { - // keyguardStateController.keyguardFadingAwayDuration might be zero when unlock by - // fingerprint due to there is no window container, see AppTransition#goodToGo. - // We use DozeParameters.wallpaperFadeOutDuration as an alternative. - duration = dozeParameters.wallpaperFadeOutDuration - startDelay = keyguardStateController.keyguardFadingAwayDelay - interpolator = Interpolators.FAST_OUT_SLOW_IN - addUpdateListener { animation: ValueAnimator -> - wakeAndUnlockBlurRadius = - blurUtils.blurRadiusOfRatio(animation.animatedValue as Float) + /** Animate blurs when unlocking. */ + private val keyguardStateCallback = + object : KeyguardStateController.Callback { + override fun onKeyguardFadingAwayChanged() { + if ( + !keyguardStateController.isKeyguardFadingAway || + biometricUnlockController.mode != MODE_WAKE_AND_UNLOCK + ) { + return } - addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - keyguardAnimator = null - wakeAndUnlockBlurRadius = 0f + + keyguardAnimator?.cancel() + keyguardAnimator = + ValueAnimator.ofFloat(1f, 0f).apply { + // keyguardStateController.keyguardFadingAwayDuration might be zero when + // unlock by fingerprint due to there is no window container, see + // AppTransition#goodToGo. We use DozeParameters.wallpaperFadeOutDuration as + // an alternative. + duration = dozeParameters.wallpaperFadeOutDuration + startDelay = keyguardStateController.keyguardFadingAwayDelay + interpolator = Interpolators.FAST_OUT_SLOW_IN + addUpdateListener { animation: ValueAnimator -> + wakeAndUnlockBlurRadius = + blurUtils.blurRadiusOfRatio(animation.animatedValue as Float) + } + addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + keyguardAnimator = null + wakeAndUnlockBlurRadius = 0f + } + } + ) + start() } - }) - start() } - } - override fun onKeyguardShowingChanged() { - if (keyguardStateController.isShowing) { - keyguardAnimator?.cancel() - notificationAnimator?.cancel() + override fun onKeyguardShowingChanged() { + if (keyguardStateController.isShowing) { + keyguardAnimator?.cancel() + notificationAnimator?.cancel() + } } } - } - private val statusBarStateCallback = object : StatusBarStateController.StateListener { - override fun onStateChanged(newState: Int) { - updateShadeAnimationBlur( - shadeExpansion, prevTracking, prevShadeVelocity, prevShadeDirection) - scheduleUpdate() - } + private val statusBarStateCallback = + object : StatusBarStateController.StateListener { + override fun onStateChanged(newState: Int) { + updateShadeAnimationBlur( + shadeExpansion, + prevTracking, + prevShadeVelocity, + prevShadeDirection, + ) + scheduleUpdate() + } - override fun onDozingChanged(isDozing: Boolean) { - if (isDozing) { - shadeAnimation.finishIfRunning() - brightnessMirrorSpring.finishIfRunning() + override fun onDozingChanged(isDozing: Boolean) { + if (isDozing) { + shadeAnimation.finishIfRunning() + brightnessMirrorSpring.finishIfRunning() + } } - } - override fun onDozeAmountChanged(linear: Float, eased: Float) { - wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased) + override fun onDozeAmountChanged(linear: Float, eased: Float) { + wakeAndUnlockBlurRadius = + if (ambientAod()) { + 0f + } else { + blurUtils.blurRadiusOfRatio(eased) + } + } } - } init { dumpManager.registerCriticalDumpable(javaClass.name, this) @@ -317,16 +334,19 @@ class NotificationShadeDepthController @Inject constructor( statusBarStateController.addCallback(statusBarStateCallback) notificationShadeWindowController.setScrimsVisibilityListener { // Stop blur effect when scrims is opaque to avoid unnecessary GPU composition. - visibility -> scrimsVisible = visibility == ScrimController.OPAQUE + visibility -> + scrimsVisible = visibility == ScrimController.OPAQUE } shadeAnimation.setStiffness(SpringForce.STIFFNESS_LOW) shadeAnimation.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY) updateResources() - configurationController.addCallback(object : ConfigurationController.ConfigurationListener { - override fun onConfigChanged(newConfig: Configuration?) { - updateResources() + configurationController.addCallback( + object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration?) { + updateResources() + } } - }) + ) } private fun updateResources() { @@ -341,15 +361,15 @@ class NotificationShadeDepthController @Inject constructor( listeners.remove(listener) } - /** - * Update blurs when pulling down the shade - */ + /** Update blurs when pulling down the shade */ override fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) { val rawFraction = event.fraction val tracking = event.tracking val timestamp = SystemClock.elapsedRealtimeNanos() - val expansion = MathUtils.saturate( - (rawFraction - panelPullDownMinFraction) / (1f - panelPullDownMinFraction)) + val expansion = + MathUtils.saturate( + (rawFraction - panelPullDownMinFraction) / (1f - panelPullDownMinFraction) + ) if (shadeExpansion == expansion && prevTracking == tracking) { prevTimestamp = timestamp @@ -360,14 +380,14 @@ class NotificationShadeDepthController @Inject constructor( if (prevTimestamp < 0) { prevTimestamp = timestamp } else { - deltaTime = MathUtils.constrain( - ((timestamp - prevTimestamp) / 1E9).toFloat(), 0.00001f, 1f) + deltaTime = + MathUtils.constrain(((timestamp - prevTimestamp) / 1E9).toFloat(), 0.00001f, 1f) } val diff = expansion - shadeExpansion val shadeDirection = sign(diff).toInt() - val shadeVelocity = MathUtils.constrain( - VELOCITY_SCALE * diff / deltaTime, MIN_VELOCITY, MAX_VELOCITY) + val shadeVelocity = + MathUtils.constrain(VELOCITY_SCALE * diff / deltaTime, MIN_VELOCITY, MAX_VELOCITY) updateShadeAnimationBlur(expansion, tracking, shadeVelocity, shadeDirection) prevShadeDirection = shadeDirection @@ -383,7 +403,7 @@ class NotificationShadeDepthController @Inject constructor( expansion: Float, tracking: Boolean, velocity: Float, - direction: Int + direction: Int, ) { if (shouldApplyShadeBlur()) { if (expansion > 0f) { @@ -432,11 +452,12 @@ class NotificationShadeDepthController @Inject constructor( private fun animateBlur(blur: Boolean, velocity: Float) { isBlurred = blur - val targetBlurNormalized = if (blur && shouldApplyShadeBlur()) { - 1f - } else { - 0f - } + val targetBlurNormalized = + if (blur && shouldApplyShadeBlur()) { + 1f + } else { + 0f + } shadeAnimation.setStartVelocity(velocity) shadeAnimation.animateTo(blurUtils.blurRadiusOfRatio(targetBlurNormalized).toInt()) @@ -453,13 +474,13 @@ class NotificationShadeDepthController @Inject constructor( } /** - * Should blur be applied to the shade currently. This is mainly used to make sure that - * on the lockscreen, the wallpaper isn't blurred. + * Should blur be applied to the shade currently. This is mainly used to make sure that on the + * lockscreen, the wallpaper isn't blurred. */ private fun shouldApplyShadeBlur(): Boolean { val state = statusBarStateController.state return (state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) && - !keyguardStateController.isKeyguardFadingAway + !keyguardStateController.isKeyguardFadingAway } override fun dump(pw: PrintWriter, args: Array<out String>) { @@ -483,33 +504,30 @@ class NotificationShadeDepthController @Inject constructor( * invalidation. */ inner class DepthAnimation() { - /** - * Blur radius visible on the UI, in pixels. - */ + /** Blur radius visible on the UI, in pixels. */ var radius = 0f - /** - * Depth ratio of the current blur radius. - */ + /** Depth ratio of the current blur radius. */ val ratio get() = blurUtils.ratioOfBlurRadius(radius) - /** - * Radius that we're animating to. - */ + /** Radius that we're animating to. */ private var pendingRadius = -1 - private var springAnimation = SpringAnimation(this, object : - FloatPropertyCompat<DepthAnimation>("blurRadius") { - override fun setValue(rect: DepthAnimation?, value: Float) { - radius = value - scheduleUpdate() - } + private var springAnimation = + SpringAnimation( + this, + object : FloatPropertyCompat<DepthAnimation>("blurRadius") { + override fun setValue(rect: DepthAnimation?, value: Float) { + radius = value + scheduleUpdate() + } - override fun getValue(rect: DepthAnimation?): Float { - return radius - } - }) + override fun getValue(rect: DepthAnimation?): Float { + return radius + } + }, + ) init { springAnimation.spring = SpringForce(0.0f) @@ -545,13 +563,9 @@ class NotificationShadeDepthController @Inject constructor( } } - /** - * Invoked when changes are needed in z-space - */ + /** Invoked when changes are needed in z-space */ interface DepthListener { - /** - * Current wallpaper zoom out, where 0 is the closest, and 1 the farthest - */ + /** Current wallpaper zoom out, where 0 is the closest, and 1 the farthest */ fun onWallpaperZoomOutChanged(zoomOut: Float) fun onBlurRadiusChanged(blurRadius: Int) {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS index 32d37aef7707..c019f308d3e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS @@ -22,3 +22,7 @@ per-file *RemoteInput* = set noparent per-file *RemoteInput* = file:notification/OWNERS per-file *EmptyShadeView* = set noparent per-file *EmptyShadeView* = file:notification/OWNERS +per-file *Lockscreen* = set noparent +per-file *Lockscreen* = file:../keyguard/OWNERS +per-file *Scrim* = set noparent +per-file *Scrim* = file:../keyguard/OWNERS diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt index be733d4e1e8e..8ce0dbf8e171 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt @@ -20,7 +20,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel import dagger.Binds import dagger.Module import dagger.Provides @@ -31,8 +31,8 @@ import dagger.multibindings.IntoMap abstract class StatusBarChipsModule { @Binds @IntoMap - @ClassKey(DemoRonChipViewModel::class) - abstract fun binds(impl: DemoRonChipViewModel): CoreStartable + @ClassKey(DemoNotifChipViewModel::class) + abstract fun binds(impl: DemoNotifChipViewModel): CoreStartable companion object { @Provides diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt index cce9a1624d51..5fa19ddef1be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel import android.content.pm.PackageManager import android.content.pm.PackageManager.NameNotFoundException @@ -22,7 +22,7 @@ import android.graphics.drawable.Drawable import com.android.systemui.CoreStartable import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel @@ -37,25 +37,25 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** - * A view model that will emit demo RON chips (rich ongoing notification chips) from [chip] based on - * adb commands sent by the user. + * A view model that will emit demo promoted ongoing notification chips from [chip] based on adb + * commands sent by the user. * * Example adb commands: * * To show a chip with the SysUI icon and custom text and color: * ``` - * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min -c "\\#434343" + * adb shell cmd statusbar demo-notif -p com.android.systemui -t 10min -c "\\#434343" * ``` * * To hide the chip: * ``` - * adb shell cmd statusbar demo-ron --hide + * adb shell cmd statusbar demo-notif --hide * ``` * - * See [DemoRonCommand] for more information on the adb command spec. + * See [DemoNotifCommand] for more information on the adb command spec. */ @SysUISingleton -class DemoRonChipViewModel +class DemoNotifChipViewModel @Inject constructor( private val commandRegistry: CommandRegistry, @@ -63,19 +63,19 @@ constructor( private val systemClock: SystemClock, ) : OngoingActivityChipViewModel, CoreStartable { override fun start() { - commandRegistry.registerCommand("demo-ron") { DemoRonCommand() } + commandRegistry.registerCommand(DEMO_COMMAND_NAME) { DemoNotifCommand() } } private val _chip = MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden()) override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow() - private inner class DemoRonCommand : ParseableCommand("demo-ron") { + private inner class DemoNotifCommand : ParseableCommand(DEMO_COMMAND_NAME) { private val packageName: String? by param( longName = "packageName", shortName = "p", - description = "The package name for the demo RON app", + description = "The package name for app \"posting\" the demo notification", valueParser = Type.String, ) @@ -99,15 +99,12 @@ constructor( ) private val hide by - flag( - longName = "hide", - description = "Hides any existing demo RON chip", - ) + flag(longName = "hide", description = "Hides any existing demo notification chip") override fun execute(pw: PrintWriter) { - if (!StatusBarRonChips.isEnabled) { + if (!StatusBarNotifChips.isEnabled) { pw.println( - "Error: com.android.systemui.status_bar_ron_chips must be enabled " + + "Error: com.android.systemui.status_bar_notification_chips must be enabled " + "before using this demo feature" ) return @@ -167,8 +164,12 @@ constructor( return null } return OngoingActivityChipModel.ChipIcon.FullColorAppIcon( - Icon.Loaded(drawable = iconDrawable, contentDescription = null), + Icon.Loaded(drawable = iconDrawable, contentDescription = null) ) } } + + companion object { + private const val DEMO_COMMAND_NAME = "demo-notif" + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt index 4ef190991f19..47ffbafe3735 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.shared +package com.android.systemui.statusbar.chips.notification.shared import com.android.systemui.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils -/** Helper for reading or using the status bar RON chips flag state. */ +/** Helper for reading or using the status bar promoted notification chips flag state. */ @Suppress("NOTHING_TO_INLINE") -object StatusBarRonChips { +object StatusBarNotifChips { /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS + const val FLAG_NAME = Flags.FLAG_STATUS_BAR_NOTIFICATION_CHIPS /** A token used for dependency declaration */ val token: FlagToken @@ -33,7 +33,7 @@ object StatusBarRonChips { /** Is the refactor enabled */ @JvmStatic inline val isEnabled - get() = Flags.statusBarRonChips() + get() = Flags.statusBarNotificationChips() /** * Called to ensure code is only run when the flag is enabled. This protects users from the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt new file mode 100644 index 000000000000..6ae92637bde7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.chips.ui.model.ColorsModel +import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** A view model for status bar chips for promoted ongoing notifications. */ +@SysUISingleton +class NotifChipsViewModel +@Inject +constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) { + /** + * A flow modeling the notification chips that should be shown. Emits an empty list if there are + * no notifications that should show a status bar chip. + */ + val chips: Flow<List<OngoingActivityChipModel.Shown>> = + activeNotificationsInteractor.promotedOngoingNotifications.map { notifications -> + notifications.mapNotNull { it.toChipModel() } + } + + /** + * Converts the notification to the [OngoingActivityChipModel] object. Returns null if the + * notification has invalid data such that it can't be displayed as a chip. + */ + private fun ActiveNotificationModel.toChipModel(): OngoingActivityChipModel.Shown? { + // TODO(b/364653005): Log error if there's no icon view. + val rawIcon = this.statusBarChipIconView ?: return null + val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(rawIcon) + // TODO(b/364653005): Use the notification color if applicable. + val colors = ColorsModel.Themed + // TODO(b/364653005): When the chip is clicked, show the HUN. + val onClickListener = null + return OngoingActivityChipModel.Shown.ShortTimeDelta( + icon, + colors, + time = this.whenTime, + onClickListener, + ) + // TODO(b/364653005): If Notification.showWhen = false, don't show the time delta. + // TODO(b/364653005): If Notification.whenTime is in the past, show "ago" in the text. + // TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`. + // TODO(b/364653005): If the app that posted the notification is in the foreground, don't + // show that app's chip. + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt index 3b1e565e122b..f4462a434477 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt @@ -21,13 +21,14 @@ import android.content.res.ColorStateList import android.graphics.drawable.GradientDrawable import android.view.View import android.view.ViewGroup +import android.widget.DateTimeView import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.res.R import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.view.ChipChronometer @@ -42,6 +43,8 @@ object OngoingActivityChipBinder { val chipTimeView: ChipChronometer = chipView.requireViewById(R.id.ongoing_activity_chip_time) val chipTextView: TextView = chipView.requireViewById(R.id.ongoing_activity_chip_text) + val chipShortTimeDeltaView: DateTimeView = + chipView.requireViewById(R.id.ongoing_activity_chip_short_time_delta) val chipBackgroundView: ChipBackgroundContainer = chipView.requireViewById(R.id.ongoing_activity_chip_background) @@ -49,13 +52,14 @@ object OngoingActivityChipBinder { is OngoingActivityChipModel.Shown -> { // Data setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView) - setChipMainContent(chipModel, chipTextView, chipTimeView) + setChipMainContent(chipModel, chipTextView, chipTimeView, chipShortTimeDeltaView) chipView.setOnClickListener(chipModel.onClickListener) updateChipPadding( chipModel, chipBackgroundView, chipTextView, chipTimeView, + chipShortTimeDeltaView, ) // Accessibility @@ -65,6 +69,7 @@ object OngoingActivityChipBinder { val textColor = chipModel.colors.text(chipContext) chipTimeView.setTextColor(textColor) chipTextView.setTextColor(textColor) + chipShortTimeDeltaView.setTextColor(textColor) (chipBackgroundView.background as GradientDrawable).color = chipModel.colors.background(chipContext) } @@ -97,7 +102,7 @@ object OngoingActivityChipBinder { defaultIconView.tintView(iconTint) } is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> { - StatusBarRonChips.assertInNewMode() + StatusBarNotifChips.assertInNewMode() IconViewBinder.bind(icon.impl, defaultIconView) defaultIconView.visibility = View.VISIBLE defaultIconView.untintView() @@ -161,6 +166,7 @@ object OngoingActivityChipBinder { chipModel: OngoingActivityChipModel.Shown, chipTextView: TextView, chipTimeView: ChipChronometer, + chipShortTimeDeltaView: DateTimeView, ) { when (chipModel) { is OngoingActivityChipModel.Shown.Countdown -> { @@ -168,21 +174,35 @@ object OngoingActivityChipBinder { chipTextView.visibility = View.VISIBLE chipTimeView.hide() + chipShortTimeDeltaView.visibility = View.GONE } is OngoingActivityChipModel.Shown.Text -> { chipTextView.text = chipModel.text chipTextView.visibility = View.VISIBLE chipTimeView.hide() + chipShortTimeDeltaView.visibility = View.GONE } is OngoingActivityChipModel.Shown.Timer -> { ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView) chipTimeView.visibility = View.VISIBLE chipTextView.visibility = View.GONE + chipShortTimeDeltaView.visibility = View.GONE + } + is OngoingActivityChipModel.Shown.ShortTimeDelta -> { + chipShortTimeDeltaView.setTime(chipModel.time) + // TODO(b/364653005): DateTimeView's relative time doesn't quite match the format we + // want in the status bar chips. + chipShortTimeDeltaView.isShowRelativeTime = true + chipShortTimeDeltaView.visibility = View.VISIBLE + + chipTextView.visibility = View.GONE + chipTimeView.hide() } is OngoingActivityChipModel.Shown.IconOnly -> { chipTextView.visibility = View.GONE + chipShortTimeDeltaView.visibility = View.GONE chipTimeView.hide() } } @@ -200,6 +220,7 @@ object OngoingActivityChipBinder { backgroundView: View, chipTextView: TextView, chipTimeView: ChipChronometer, + chipShortTimeDeltaView: DateTimeView, ) { if (chipModel.icon != null) { if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) { @@ -209,15 +230,18 @@ object OngoingActivityChipBinder { backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon() chipTextView.setTextPaddingForEmbeddedPaddingIcon() chipTimeView.setTextPaddingForEmbeddedPaddingIcon() + chipShortTimeDeltaView.setTextPaddingForEmbeddedPaddingIcon() } else { backgroundView.setBackgroundPaddingForNormalIcon() chipTextView.setTextPaddingForNormalIcon() chipTimeView.setTextPaddingForNormalIcon() + chipShortTimeDeltaView.setTextPaddingForNormalIcon() } } else { backgroundView.setBackgroundPaddingForNoIcon() chipTextView.setTextPaddingForNoIcon() chipTimeView.setTextPaddingForNoIcon() + chipShortTimeDeltaView.setTextPaddingForNoIcon() } } @@ -258,23 +282,13 @@ object OngoingActivityChipBinder { context.resources.getDimensionPixelSize( R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon ) - setPaddingRelative( - sidePadding, - paddingTop, - sidePadding, - paddingBottom, - ) + setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom) } private fun View.setBackgroundPaddingForNormalIcon() { val sidePadding = context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_side_padding) - setPaddingRelative( - sidePadding, - paddingTop, - sidePadding, - paddingBottom, - ) + setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom) } private fun View.setBackgroundPaddingForNoIcon() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt index 62622a893ec5..cf07af1fc5dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt @@ -20,6 +20,7 @@ import android.view.View import com.android.systemui.Flags import com.android.systemui.common.shared.model.Icon import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips /** Model representing the display of an ongoing activity as a chip in the status bar. */ sealed class OngoingActivityChipModel { @@ -79,6 +80,24 @@ sealed class OngoingActivityChipModel { } /** + * The chip shows the time delta between now and [time] in a short format, e.g. "15min" or + * "1hr ago". + */ + data class ShortTimeDelta( + override val icon: ChipIcon, + override val colors: ColorsModel, + /** The time of the event that this chip represents. */ + val time: Long, + override val onClickListener: View.OnClickListener?, + ) : Shown(icon, colors, onClickListener) { + init { + StatusBarNotifChips.assertInNewMode() + } + + override val logName = "Shown.ShortTimeDelta" + } + + /** * This chip shows a countdown using [secondsUntilStarted]. Used to inform users that an * event is about to start. Typically, a [Countdown] chip will turn into a [Timer] chip. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt index 24c1b879f429..ed325970ebb2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.chips.ui.viewmodel -import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer @@ -24,19 +23,20 @@ import com.android.systemui.log.core.LogLevel import com.android.systemui.statusbar.chips.StatusBarChipsLog import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.util.kotlin.combine import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -55,7 +55,8 @@ constructor( shareToAppChipViewModel: ShareToAppChipViewModel, castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel, callChipViewModel: CallChipViewModel, - demoRonChipViewModel: DemoRonChipViewModel, + notifChipsViewModel: NotifChipsViewModel, + demoNotifChipViewModel: DemoNotifChipViewModel, @StatusBarChipsLog private val logger: LogBuffer, ) { private enum class ChipType { @@ -63,8 +64,9 @@ constructor( ShareToApp, CastToOtherDevice, Call, - /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */ - DemoRon, + Notification, + /** A demo of a notification chip, used just for testing. */ + DemoNotification, } /** Model that helps us internally track the various chip states from each of the types. */ @@ -85,7 +87,8 @@ constructor( val shareToApp: OngoingActivityChipModel.Hidden, val castToOtherDevice: OngoingActivityChipModel.Hidden, val call: OngoingActivityChipModel.Hidden, - val demoRon: OngoingActivityChipModel.Hidden, + val notifs: OngoingActivityChipModel.Hidden, + val demoNotif: OngoingActivityChipModel.Hidden, ) : InternalChipModel } @@ -94,7 +97,8 @@ constructor( val shareToApp: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), val castToOtherDevice: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), val call: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), - val demoRon: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), + val notifs: List<OngoingActivityChipModel.Shown> = emptyList(), + val demoNotif: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), ) /** Bundles all the incoming chips into one object to easily pass to various flows. */ @@ -104,8 +108,9 @@ constructor( shareToAppChipViewModel.chip, castToOtherDeviceChipViewModel.chip, callChipViewModel.chip, - demoRonChipViewModel.chip, - ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon -> + notifChipsViewModel.chips, + demoNotifChipViewModel.chip, + ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoNotif -> logger.log( TAG, LogLevel.INFO, @@ -121,16 +126,19 @@ constructor( LogLevel.INFO, { str1 = call.logName - str2 = demoRon.logName + // TODO(b/364653005): Log other information for notification chips. + str2 = notifs.map { it.logName }.toString() + str3 = demoNotif.logName }, - { "... > Call=$str1 > DemoRon=$str2" } + { "... > Call=$str1 > Notifs=$str2 > DemoNotif=$str3" }, ) ChipBundle( screenRecord = screenRecord, shareToApp = shareToApp, castToOtherDevice = castToOtherDevice, call = call, - demoRon = demoRon, + notifs = notifs, + demoNotif = demoNotif, ) } // Some of the chips could have timers in them and we don't want the start time @@ -189,9 +197,9 @@ constructor( * actually displaying the chip. */ val chips: StateFlow<MultipleOngoingActivityChipsModel> = - if (!Flags.statusBarRonChips()) { - // Multiple chips are only allowed with RONs. If the flag isn't on, use just the - // primary chip. + if (!StatusBarNotifChips.isEnabled) { + // Multiple chips are only allowed with notification chips. If the flag isn't on, use + // just the primary chip. primaryChip .map { MultipleOngoingActivityChipsModel( @@ -199,11 +207,7 @@ constructor( secondary = OngoingActivityChipModel.Hidden(), ) } - .stateIn( - scope, - SharingStarted.Lazily, - MultipleOngoingActivityChipsModel(), - ) + .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel()) } else { internalChips .pairwise(initialValue = DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL) @@ -212,11 +216,7 @@ constructor( val correctSecondary = createOutputModel(old.secondary, new.secondary) MultipleOngoingActivityChipsModel(correctPrimary, correctSecondary) } - .stateIn( - scope, - SharingStarted.Lazily, - MultipleOngoingActivityChipsModel(), - ) + .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel()) } /** A data class representing the return result of [pickMostImportantChip]. */ @@ -251,7 +251,7 @@ constructor( // suppress the share-to-app chip to make sure they don't both show. // See b/296461748. shareToApp = OngoingActivityChipModel.Hidden(), - ) + ), ) bundle.shareToApp is OngoingActivityChipModel.Shown -> MostImportantChipResult( @@ -274,11 +274,19 @@ constructor( mostImportantChip = InternalChipModel.Shown(ChipType.Call, bundle.call), remainingChips = bundle.copy(call = OngoingActivityChipModel.Hidden()), ) - bundle.demoRon is OngoingActivityChipModel.Shown -> { - StatusBarRonChips.assertInNewMode() + bundle.notifs.isNotEmpty() -> + MostImportantChipResult( + mostImportantChip = + InternalChipModel.Shown(ChipType.Notification, bundle.notifs.first()), + remainingChips = + bundle.copy(notifs = bundle.notifs.subList(1, bundle.notifs.size)), + ) + bundle.demoNotif is OngoingActivityChipModel.Shown -> { + StatusBarNotifChips.assertInNewMode() MostImportantChipResult( - mostImportantChip = InternalChipModel.Shown(ChipType.DemoRon, bundle.demoRon), - remainingChips = bundle.copy(demoRon = OngoingActivityChipModel.Hidden()), + mostImportantChip = + InternalChipModel.Shown(ChipType.DemoNotification, bundle.demoNotif), + remainingChips = bundle.copy(demoNotif = OngoingActivityChipModel.Hidden()), ) } else -> { @@ -287,7 +295,8 @@ constructor( check(bundle.shareToApp is OngoingActivityChipModel.Hidden) check(bundle.castToOtherDevice is OngoingActivityChipModel.Hidden) check(bundle.call is OngoingActivityChipModel.Hidden) - check(bundle.demoRon is OngoingActivityChipModel.Hidden) + check(bundle.notifs.isEmpty()) + check(bundle.demoNotif is OngoingActivityChipModel.Hidden) MostImportantChipResult( mostImportantChip = InternalChipModel.Hidden( @@ -295,7 +304,8 @@ constructor( shareToApp = bundle.shareToApp, castToOtherDevice = bundle.castToOtherDevice, call = bundle.call, - demoRon = bundle.demoRon, + notifs = OngoingActivityChipModel.Hidden(), + demoNotif = bundle.demoNotif, ), // All the chips are already hidden, so no need to filter anything out of the // bundle. @@ -323,7 +333,8 @@ constructor( ChipType.ShareToApp -> new.shareToApp ChipType.CastToOtherDevice -> new.castToOtherDevice ChipType.Call -> new.call - ChipType.DemoRon -> new.demoRon + ChipType.Notification -> new.notifs + ChipType.DemoNotification -> new.demoNotif } } else if (new is InternalChipModel.Shown) { // If we have a chip to show, always show it. @@ -344,7 +355,8 @@ constructor( shareToApp = OngoingActivityChipModel.Hidden(), castToOtherDevice = OngoingActivityChipModel.Hidden(), call = OngoingActivityChipModel.Hidden(), - demoRon = OngoingActivityChipModel.Hidden(), + notifs = OngoingActivityChipModel.Hidden(), + demoNotif = OngoingActivityChipModel.Hidden(), ) private val DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt new file mode 100644 index 000000000000..b64a577ef77e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.core + +import android.view.Display +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.display.data.repository.DisplayRepository +import com.android.systemui.display.data.repository.DisplayScopeRepository +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore +import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore +import com.android.systemui.util.kotlin.pairwiseBy +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch + +/** + * Responsible for creating and starting the status bar components for each display. Also does it + * for newly added displays. + */ +@SysUISingleton +class MultiDisplayStatusBarStarter +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + private val displayScopeRepository: DisplayScopeRepository, + private val statusBarOrchestratorFactory: StatusBarOrchestrator.Factory, + private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore, + private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore, + private val displayRepository: DisplayRepository, + private val initializerStore: StatusBarInitializerStore, + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, + private val statusBarInitializerStore: StatusBarInitializerStore, +) : CoreStartable { + + init { + StatusBarConnectedDisplays.assertInNewMode() + } + + override fun start() { + applicationScope.launch { + displayRepository.displays + .pairwiseBy { previousDisplays, currentDisplays -> + currentDisplays - previousDisplays + } + .onStart { emit(displayRepository.displays.value) } + .collect { newDisplays -> + newDisplays.forEach { createAndStartComponentsForDisplay(it) } + } + } + } + + private fun createAndStartComponentsForDisplay(display: Display) { + val displayId = display.displayId + createAndStartOrchestratorForDisplay(displayId) + createAndStartInitializerForDisplay(displayId) + } + + private fun createAndStartOrchestratorForDisplay(displayId: Int) { + statusBarOrchestratorFactory + .create( + displayId, + displayScopeRepository.scopeForDisplay(displayId), + statusBarWindowStateRepositoryStore.forDisplay(displayId), + statusBarModeRepositoryStore.forDisplay(displayId), + initializerStore.forDisplay(displayId), + statusBarWindowControllerStore.forDisplay(displayId), + ) + .start() + } + + private fun createAndStartInitializerForDisplay(displayId: Int) { + statusBarInitializerStore.forDisplay(displayId).start() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt index 7eff8124368b..6201ca553398 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.core import android.app.Fragment +import android.view.ViewGroup import androidx.annotation.VisibleForTesting import com.android.systemui.CoreStartable import com.android.systemui.fragments.FragmentHostManager @@ -23,10 +24,12 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions +import com.android.systemui.statusbar.phone.PhoneStatusBarView import com.android.systemui.statusbar.phone.PhoneStatusBarViewController import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent -import com.android.systemui.statusbar.window.StatusBarWindowControllerStore +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent +import com.android.systemui.statusbar.pipeline.shared.ui.composable.StatusBarRootFactory +import com.android.systemui.statusbar.window.StatusBarWindowController import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -37,7 +40,7 @@ import javax.inject.Provider * Responsible for creating the status bar window and initializing the root components of that * window (see [CollapsedStatusBarFragment]) */ -interface StatusBarInitializer { +interface StatusBarInitializer : CoreStartable { var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? @@ -57,7 +60,7 @@ interface StatusBarInitializer { * Can be used to retrieve dependencies from that scope, including the status bar root * view. */ - fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) + fun onStatusBarViewInitialized(component: HomeStatusBarComponent) } interface OnStatusBarViewUpdatedListener { @@ -68,19 +71,20 @@ interface StatusBarInitializer { } interface Factory { - fun create(displayId: Int): StatusBarInitializer + fun create(statusBarWindowController: StatusBarWindowController): StatusBarInitializer } } class StatusBarInitializerImpl @AssistedInject constructor( - @Assisted private val displayId: Int, - private val statusBarWindowControllerStore: StatusBarWindowControllerStore, + @Assisted private val statusBarWindowController: StatusBarWindowController, private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>, + private val statusBarRootFactory: StatusBarRootFactory, + private val componentFactory: HomeStatusBarComponent.Factory, private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>, -) : CoreStartable, StatusBarInitializer { - private var component: StatusBarFragmentComponent? = null +) : StatusBarInitializer { + private var component: HomeStatusBarComponent? = null @get:VisibleForTesting var initialized = false @@ -110,21 +114,57 @@ constructor( } private fun doStart() { + if (StatusBarSimpleFragment.isEnabled) doComposeStart() else doLegacyStart() + } + + /** + * Stand up the [PhoneStatusBarView] in a compose root. There will be no + * [CollapsedStatusBarFragment] in this mode + */ + private fun doComposeStart() { + initialized = true + val statusBarRoot = + statusBarRootFactory.create(statusBarWindowController.backgroundView as ViewGroup) { cv + -> + val phoneStatusBarView = cv.findViewById<PhoneStatusBarView>(R.id.status_bar) + component = + componentFactory.create(phoneStatusBarView).also { component -> + // CollapsedStatusBarFragment used to be responsible initializting + component.init() + + statusBarViewUpdatedListener?.onStatusBarViewUpdated( + component.phoneStatusBarViewController, + component.phoneStatusBarTransitions, + ) + + creationListeners.forEach { listener -> + listener.onStatusBarViewInitialized(component) + } + } + } + + // Add the new compose view to the hierarchy because we don't use fragment transactions + // anymore + val windowBackgroundView = statusBarWindowController.backgroundView as ViewGroup + windowBackgroundView.addView(statusBarRoot) + } + + private fun doLegacyStart() { initialized = true - statusBarWindowControllerStore.defaultDisplay.fragmentHostManager + statusBarWindowController.fragmentHostManager .addTagListener( CollapsedStatusBarFragment.TAG, object : FragmentHostManager.FragmentListener { override fun onFragmentViewCreated(tag: String, fragment: Fragment) { - val statusBarFragmentComponent = - (fragment as CollapsedStatusBarFragment).statusBarFragmentComponent + component = + (fragment as CollapsedStatusBarFragment).homeStatusBarComponent ?: throw IllegalStateException() statusBarViewUpdatedListener?.onStatusBarViewUpdated( - statusBarFragmentComponent.phoneStatusBarViewController, - statusBarFragmentComponent.phoneStatusBarTransitions, + component!!.phoneStatusBarViewController, + component!!.phoneStatusBarTransitions, ) creationListeners.forEach { listener -> - listener.onStatusBarViewInitialized(statusBarFragmentComponent) + listener.onStatusBarViewInitialized(component!!) } } @@ -145,6 +185,8 @@ constructor( @AssistedFactory interface Factory : StatusBarInitializer.Factory { - override fun create(displayId: Int): StatusBarInitializerImpl + override fun create( + statusBarWindowController: StatusBarWindowController + ): StatusBarInitializerImpl } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt index 8d044bb9ce87..041f0b0fdf93 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt @@ -16,84 +16,52 @@ package com.android.systemui.statusbar.core -import android.view.Display -import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.repository.DisplayRepository -import java.util.concurrent.ConcurrentHashMap +import com.android.systemui.display.data.repository.PerDisplayStore +import com.android.systemui.display.data.repository.PerDisplayStoreImpl +import com.android.systemui.display.data.repository.SingleDisplayStore +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import javax.inject.Inject -import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch /** Provides per display instances of [StatusBarInitializer]. */ -interface StatusBarInitializerStore { - /** - * The instance for the default/main display of the device. For example, on a phone or a tablet, - * the default display is the internal/built-in display of the device. - * - * Note that the id of the default display is [Display.DEFAULT_DISPLAY]. - */ - val defaultDisplay: StatusBarInitializer - - /** - * Returns an instance for a specific display id. - * - * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing - * displays. - */ - fun forDisplay(displayId: Int): StatusBarInitializer -} +interface StatusBarInitializerStore : PerDisplayStore<StatusBarInitializer> @SysUISingleton class MultiDisplayStatusBarInitializerStore @Inject constructor( - @Background private val backgroundApplicationScope: CoroutineScope, + @Background backgroundApplicationScope: CoroutineScope, + displayRepository: DisplayRepository, private val factory: StatusBarInitializer.Factory, - private val displayRepository: DisplayRepository, -) : StatusBarInitializerStore, CoreStartable { + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, +) : + StatusBarInitializerStore, + PerDisplayStoreImpl<StatusBarInitializer>(backgroundApplicationScope, displayRepository) { init { StatusBarConnectedDisplays.assertInNewMode() } - private val perDisplayInitializers = ConcurrentHashMap<Int, StatusBarInitializer>() - - override val defaultDisplay: StatusBarInitializer - get() = forDisplay(Display.DEFAULT_DISPLAY) - - override fun forDisplay(displayId: Int): StatusBarInitializer { - if (displayRepository.getDisplay(displayId) == null) { - throw IllegalArgumentException("Display with id $displayId doesn't exist.") - } - return perDisplayInitializers.computeIfAbsent(displayId) { factory.create(displayId) } + override fun createInstanceForDisplay(displayId: Int): StatusBarInitializer { + return factory.create( + statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId) + ) } - override fun start() { - backgroundApplicationScope.launch( - CoroutineName("MultiDisplayStatusBarInitializerStore#start") - ) { - displayRepository.displayRemovalEvent.collect { removedDisplayId -> - perDisplayInitializers.remove(removedDisplayId) - } - } - } + override val instanceClass = StatusBarInitializer::class.java } @SysUISingleton class SingleDisplayStatusBarInitializerStore @Inject -constructor(factory: StatusBarInitializerImpl.Factory) : StatusBarInitializerStore { +constructor(defaultInitializer: StatusBarInitializer) : + StatusBarInitializerStore, + PerDisplayStore<StatusBarInitializer> by SingleDisplayStore(defaultInitializer) { init { StatusBarConnectedDisplays.assertInLegacyMode() } - - private val defaultInstance = factory.create(Display.DEFAULT_DISPLAY) - - override val defaultDisplay: StatusBarInitializer = defaultInstance - - override fun forDisplay(displayId: Int): StatusBarInitializer = defaultDisplay } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt index d372eb29b27b..5e59745cad29 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt @@ -16,12 +16,12 @@ package com.android.systemui.statusbar.core +import android.view.Display import android.view.View import com.android.systemui.CoreStartable import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.demomode.DemoModeController +import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.plugins.PluginDependencyProvider import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -31,19 +31,21 @@ import com.android.systemui.shade.ShadeSurface import com.android.systemui.statusbar.AutoHideUiElement import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.data.model.StatusBarMode -import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore +import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository import com.android.systemui.statusbar.phone.AutoHideController import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions import com.android.systemui.statusbar.phone.PhoneStatusBarViewController -import com.android.systemui.statusbar.window.StatusBarWindowControllerStore +import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.data.model.StatusBarWindowState -import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore +import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository import com.android.wm.shell.bubbles.Bubbles import dagger.Lazy +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import java.io.PrintWriter import java.util.Optional -import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -57,27 +59,35 @@ import kotlinx.coroutines.launch * It is a temporary class, created to pull status bar related logic out of CentralSurfacesImpl. The * plan is break it out into individual classes. */ -@SysUISingleton class StatusBarOrchestrator -@Inject +@AssistedInject constructor( - @Application private val applicationScope: CoroutineScope, - private val statusBarInitializer: StatusBarInitializer, - private val statusBarModeRepository: StatusBarModeRepositoryStore, - private val statusBarWindowControllerStore: StatusBarWindowControllerStore, + @Assisted private val displayId: Int, + @Assisted private val coroutineScope: CoroutineScope, + @Assisted private val statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository, + @Assisted private val statusBarModeRepository: StatusBarModePerDisplayRepository, + @Assisted private val statusBarInitializer: StatusBarInitializer, + @Assisted private val statusBarWindowController: StatusBarWindowController, private val demoModeController: DemoModeController, private val pluginDependencyProvider: PluginDependencyProvider, private val autoHideController: AutoHideController, private val remoteInputManager: NotificationRemoteInputManager, private val notificationShadeWindowViewControllerLazy: - Lazy<NotificationShadeWindowViewController>, + Lazy<NotificationShadeWindowViewController>, private val shadeSurface: ShadeSurface, private val bubblesOptional: Optional<Bubbles>, - private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore, + private val dumpManager: DumpManager, powerInteractor: PowerInteractor, primaryBouncerInteractor: PrimaryBouncerInteractor, ) : CoreStartable { + private val dumpableName: String = + if (displayId == Display.DEFAULT_DISPLAY) { + javaClass.simpleName + } else { + "${javaClass.simpleName}$displayId" + } + private val phoneStatusBarViewController = MutableStateFlow<PhoneStatusBarViewController?>(value = null) @@ -86,9 +96,9 @@ constructor( private val shouldAnimateNextBarModeChange = combine( - statusBarModeRepository.defaultDisplay.isTransientShown, + statusBarModeRepository.isTransientShown, powerInteractor.isAwake, - statusBarWindowStateRepositoryStore.defaultDisplay.windowState, + statusBarWindowStateRepository.windowState, ) { isTransientShown, isDeviceAwake, statusBarWindowState -> !isTransientShown && isDeviceAwake && @@ -107,8 +117,8 @@ constructor( private val statusBarVisible = combine( - statusBarModeRepository.defaultDisplay.statusBarMode, - statusBarWindowStateRepositoryStore.defaultDisplay.windowState, + statusBarModeRepository.statusBarMode, + statusBarWindowStateRepository.windowState, ) { mode, statusBarWindowState -> mode != StatusBarMode.LIGHTS_OUT && mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT && @@ -119,7 +129,7 @@ constructor( combine( shouldAnimateNextBarModeChange, phoneStatusBarTransitions.filterNotNull(), - statusBarModeRepository.defaultDisplay.statusBarMode, + statusBarModeRepository.statusBarMode, ::Triple, ) .distinctUntilChangedBy { (_, barTransitions, statusBarMode) -> @@ -130,37 +140,40 @@ constructor( override fun start() { StatusBarSimpleFragment.assertInNewMode() - applicationScope.launch { - launch { - controllerAndBouncerShowing.collect { (controller, bouncerShowing) -> - setBouncerShowingForStatusBarComponents(controller, bouncerShowing) + coroutineScope + .launch { + dumpManager.registerCriticalDumpable(dumpableName, this@StatusBarOrchestrator) + launch { + controllerAndBouncerShowing.collect { (controller, bouncerShowing) -> + setBouncerShowingForStatusBarComponents(controller, bouncerShowing) + } } - } - launch { - barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) -> - if (deviceAsleep) { - barTransitions.finishAnimations() + launch { + barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) -> + if (deviceAsleep) { + barTransitions.finishAnimations() + } } } - } - launch { statusBarVisible.collect { updateBubblesVisibility(it) } } - launch { - barModeUpdate.collect { (animate, barTransitions, statusBarMode) -> - updateBarMode(animate, barTransitions, statusBarMode) + launch { statusBarVisible.collect { updateBubblesVisibility(it) } } + launch { + barModeUpdate.collect { (animate, barTransitions, statusBarMode) -> + updateBarMode(animate, barTransitions, statusBarMode) + } } } - } + .invokeOnCompletion { dumpManager.unregisterDumpable(dumpableName) } createAndAddWindow() setupPluginDependencies() setUpAutoHide() } private fun createAndAddWindow() { - initializeStatusBarFragment() - statusBarWindowControllerStore.defaultDisplay.attach() + initializeStatusBarRootView() + statusBarWindowController.attach() } - private fun initializeStatusBarFragment() { + private fun initializeStatusBarRootView() { statusBarInitializer.statusBarViewUpdatedListener = object : StatusBarInitializer.OnStatusBarViewUpdatedListener { override fun onStatusBarViewUpdated( @@ -170,6 +183,10 @@ constructor( phoneStatusBarViewController.value = statusBarViewController phoneStatusBarTransitions.value = statusBarTransitions + if (displayId != Display.DEFAULT_DISPLAY) { + return + } + // TODO(b/373310629): shade should be display id aware notificationShadeWindowViewControllerLazy .get() .setStatusBarViewController(statusBarViewController) @@ -189,6 +206,10 @@ constructor( } private fun setUpAutoHide() { + if (displayId != Display.DEFAULT_DISPLAY) { + return + } + // TODO(b/373309973): per display implementation of auto hide controller autoHideController.setStatusBar( object : AutoHideUiElement { override fun synchronizeState() {} @@ -198,13 +219,14 @@ constructor( } override fun isVisible(): Boolean { - return statusBarModeRepository.defaultDisplay.isTransientShown.value + return statusBarModeRepository.isTransientShown.value } override fun hide() { - statusBarModeRepository.defaultDisplay.clearTransient() + statusBarModeRepository.clearTransient() } - }) + } + ) } private fun updateBarMode( @@ -215,11 +237,18 @@ constructor( if (!demoModeController.isInDemoMode) { barTransitions.transitionTo(barMode.toTransitionModeInt(), animate) } - autoHideController.touchAutoHide() + if (displayId == Display.DEFAULT_DISPLAY) { + // TODO(b/373309973): per display implementation of auto hide controller + autoHideController.touchAutoHide() + } } private fun updateBubblesVisibility(statusBarVisible: Boolean) { + if (displayId != Display.DEFAULT_DISPLAY) { + return + } bubblesOptional.ifPresent { bubbles: Bubbles -> + // TODO(b/373311537): per display implementation of Bubbles bubbles.onStatusBarVisibilityChanged(statusBarVisible) } } @@ -238,11 +267,23 @@ constructor( } override fun dump(pw: PrintWriter, args: Array<out String>) { - pw.println(statusBarWindowStateRepositoryStore.defaultDisplay.windowState.value) + pw.println(statusBarWindowStateRepository.windowState.value) CentralSurfaces.dumpBarTransitions( pw, "PhoneStatusBarTransitions", phoneStatusBarTransitions.value, ) } + + @AssistedFactory + interface Factory { + fun create( + displayId: Int, + displayScope: CoroutineScope, + statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository, + statusBarModeRepository: StatusBarModePerDisplayRepository, + statusBarInitializer: StatusBarInitializer, + statusBarWindowController: StatusBarWindowController, + ): StatusBarOrchestrator + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt index 5aad11fe1034..f6f4503b210a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt @@ -17,17 +17,20 @@ package com.android.systemui.statusbar.dagger import android.content.Context -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.CoreStartable +import com.android.systemui.SysUICutoutProvider import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.data.StatusBarDataLayerModule import com.android.systemui.statusbar.phone.LightBarController +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl import com.android.systemui.statusbar.phone.StatusBarSignalPolicy import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog +import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl import com.android.systemui.statusbar.window.MultiDisplayStatusBarWindowControllerStore import com.android.systemui.statusbar.window.SingleDisplayStatusBarWindowControllerStore @@ -108,5 +111,16 @@ abstract class StatusBarModule { fun provideOngoingCallLogBuffer(factory: LogBufferFactory): LogBuffer { return factory.create("OngoingCall", 75) } + + @Provides + @SysUISingleton + fun contentInsetsProvider( + factory: StatusBarContentInsetsProviderImpl.Factory, + context: Context, + configurationController: ConfigurationController, + sysUICutoutProvider: SysUICutoutProvider, + ): StatusBarContentInsetsProvider { + return factory.create(context, configurationController, sysUICutoutProvider) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt index 9f878b241d73..e6270b8740a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.data import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerModule import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryModule import com.android.systemui.statusbar.phone.data.StatusBarPhoneDataLayerModule import dagger.Module @@ -26,8 +27,9 @@ import dagger.Module [ KeyguardStatusBarRepositoryModule::class, RemoteInputRepositoryModule::class, + StatusBarConfigurationControllerModule::class, StatusBarModeRepositoryModule::class, - StatusBarPhoneDataLayerModule::class + StatusBarPhoneDataLayerModule::class, ] ) object StatusBarDataLayerModule diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt new file mode 100644 index 000000000000..280d66bcb827 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.data.repository + +import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR +import com.android.systemui.CoreStartable +import com.android.systemui.common.ui.GlobalConfig +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.display.data.repository.DisplayRepository +import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository +import com.android.systemui.display.data.repository.PerDisplayStore +import com.android.systemui.display.data.repository.PerDisplayStoreImpl +import com.android.systemui.display.data.repository.SingleDisplayStore +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays +import com.android.systemui.statusbar.phone.ConfigurationControllerImpl +import com.android.systemui.statusbar.policy.ConfigurationController +import dagger.Lazy +import dagger.Module +import dagger.Provides +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope + +/** Status bar specific interface to disambiguate from the global [ConfigurationController]. */ +interface StatusBarConfigurationController : ConfigurationController + +/** Provides per display instances of [ConfigurationController], specifically for the Status Bar. */ +interface StatusBarConfigurationControllerStore : PerDisplayStore<StatusBarConfigurationController> + +@SysUISingleton +class MultiDisplayStatusBarConfigurationControllerStore +@Inject +constructor( + @Background backgroundApplicationScope: CoroutineScope, + displayRepository: DisplayRepository, + private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, + private val configurationControllerFactory: ConfigurationControllerImpl.Factory, +) : + StatusBarConfigurationControllerStore, + PerDisplayStoreImpl<StatusBarConfigurationController>( + backgroundApplicationScope, + displayRepository, + ) { + + init { + StatusBarConnectedDisplays.assertInNewMode() + } + + override fun createInstanceForDisplay(displayId: Int): StatusBarConfigurationController { + val displayWindowProperties = + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) + return configurationControllerFactory.create(displayWindowProperties.context) + } + + override val instanceClass = StatusBarConfigurationController::class.java +} + +@SysUISingleton +class SingleDisplayStatusBarConfigurationControllerStore +@Inject +constructor(@GlobalConfig globalConfigurationController: ConfigurationController) : + StatusBarConfigurationControllerStore, + PerDisplayStore<StatusBarConfigurationController> by SingleDisplayStore( + globalConfigurationController as StatusBarConfigurationController + ) { + + init { + StatusBarConnectedDisplays.assertInLegacyMode() + } +} + +@Module +object StatusBarConfigurationControllerModule { + + @Provides + @SysUISingleton + fun store( + singleDisplayLazy: Lazy<SingleDisplayStatusBarConfigurationControllerStore>, + multiDisplayLazy: Lazy<MultiDisplayStatusBarConfigurationControllerStore>, + ): StatusBarConfigurationControllerStore { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayLazy.get() + } else { + singleDisplayLazy.get() + } + } + + @Provides + @SysUISingleton + @IntoMap + @ClassKey(StatusBarConfigurationControllerStore::class) + fun storeAsCoreStartable( + multiDisplayLazy: Lazy<MultiDisplayStatusBarConfigurationControllerStore> + ): CoreStartable { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayLazy.get() + } else { + CoreStartable.NOP + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt index 088c86df8437..44bee1d784fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.data.model.StatusBarMode import com.android.systemui.statusbar.phone.BoundsPair import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator import com.android.systemui.statusbar.phone.StatusBarBoundsProvider -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel import dagger.assisted.Assisted @@ -174,7 +174,7 @@ constructor( private val _statusBarBounds = MutableStateFlow(BoundsPair(Rect(), Rect())) - override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) { + override fun onStatusBarViewInitialized(component: HomeStatusBarComponent) { val statusBarBoundsProvider = component.boundsProvider val listener = object : StatusBarBoundsProvider.BoundsChangeListener { @@ -196,10 +196,9 @@ constructor( /** Modifies the raw [StatusBarAttributes] if letterboxing is needed. */ private val modifiedStatusBarAttributes: StateFlow<ModifiedStatusBarAttributes?> = - combine( - _originalStatusBarAttributes, - _statusBarBounds, - ) { originalAttributes, statusBarBounds -> + combine(_originalStatusBarAttributes, _statusBarBounds) { + originalAttributes, + statusBarBounds -> if (originalAttributes == null) { null } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt index 962cb0953f97..2c9fa25d8535 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt @@ -20,7 +20,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.statusbar.core.StatusBarInitializer -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent import dagger.Binds import dagger.Module import dagger.multibindings.ClassKey @@ -31,6 +31,7 @@ import javax.inject.Inject interface StatusBarModeRepositoryStore { val defaultDisplay: StatusBarModePerDisplayRepository + fun forDisplay(displayId: Int): StatusBarModePerDisplayRepository } @@ -39,7 +40,7 @@ class StatusBarModeRepositoryImpl @Inject constructor( @DisplayId private val displayId: Int, - factory: StatusBarModePerDisplayRepositoryFactory + factory: StatusBarModePerDisplayRepositoryFactory, ) : StatusBarModeRepositoryStore, CoreStartable, @@ -47,17 +48,15 @@ constructor( override val defaultDisplay = factory.create(displayId) override fun forDisplay(displayId: Int) = - if (this.displayId == displayId) { - defaultDisplay - } else { - TODO("b/127878649 implement multi-display state management") - } + // TODO(b/369337087): implement per display status bar modes. + // For now just use default display instance. + defaultDisplay override fun start() { defaultDisplay.start() } - override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) { + override fun onStatusBarViewInitialized(component: HomeStatusBarComponent) { defaultDisplay.onStatusBarViewInitialized(component) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java index 0927a728783f..a61463823613 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java @@ -115,23 +115,23 @@ public class HeadsUpTouchHelper implements Gefingerpoken { final float h = y - mInitialTouchY; if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)) { - setTrackingHeadsUp(true); - mCollapseSnoozes = h < 0; - mInitialTouchX = x; - mInitialTouchY = y; - int startHeight = (int) (mPickedChild.getActualHeight() - + mPickedChild.getTranslationY()); - mPanel.setHeadsUpDraggingStartingHeight(startHeight); - mPanel.startExpand(x, y, true /* startTracking */, startHeight); - if (!SceneContainerFlag.isEnabled()) { + setTrackingHeadsUp(true); + mCollapseSnoozes = h < 0; + mInitialTouchX = x; + mInitialTouchY = y; + int startHeight = (int) (mPickedChild.getActualHeight() + + mPickedChild.getTranslationY()); + mPanel.setHeadsUpDraggingStartingHeight(startHeight); + mPanel.startExpand(x, y, true /* startTracking */, startHeight); + // This call needs to be after the expansion start otherwise we will get a // flicker of one frame as it's not expanded yet. mHeadsUpManager.unpinAll(true); - } - clearNotificationEffects(); - endMotion(); + clearNotificationEffects(); + endMotion(); + } return true; } break; @@ -167,17 +167,70 @@ public class HeadsUpTouchHelper implements Gefingerpoken { @Override public boolean onTouchEvent(MotionEvent event) { - if (!mTrackingHeadsUp) { + if (SceneContainerFlag.isEnabled()) { + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float x = event.getX(pointerIndex); + final float y = event.getY(pointerIndex); + switch (event.getActionMasked()) { + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + mTrackingPointer = event.getPointerId(newIndex); + mInitialTouchX = event.getX(newIndex); + mInitialTouchY = event.getY(newIndex); + } + break; + case MotionEvent.ACTION_MOVE: + final float h = y - mInitialTouchY; + if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop + && Math.abs(h) > Math.abs(x - mInitialTouchX)) { + setTrackingHeadsUp(true); + mCollapseSnoozes = h < 0; + mInitialTouchX = x; + mInitialTouchY = y; + int startHeight = (int) (mPickedChild.getActualHeight() + + mPickedChild.getTranslationY()); + mPanel.setHeadsUpDraggingStartingHeight(startHeight); + mPanel.startExpand(x, y, true /* startTracking */, startHeight); + + clearNotificationEffects(); + endMotion(); + return true; + } + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (mPickedChild != null && mTouchingHeadsUpView) { + // We may swallow this click if the heads up just came in. + if (mHeadsUpManager.shouldSwallowClick( + mPickedChild.getEntry().getSbn().getKey())) { + endMotion(); + return true; + } + } + endMotion(); + return false; + } return false; + } else { + if (!mTrackingHeadsUp) { + return false; + } + switch (event.getActionMasked()) { + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + endMotion(); + setTrackingHeadsUp(false); + break; + } + return true; } - switch (event.getActionMasked()) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - endMotion(); - setTrackingHeadsUp(false); - break; - } - return true; } private void endMotion() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinator.kt deleted file mode 100644 index d268e358690f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinator.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification.collection.coordinator - -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.keyguard.data.repository.KeyguardRepository -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.statusbar.SysuiStatusBarStateController -import com.android.systemui.statusbar.notification.collection.NotifPipeline -import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter -import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -/** - * Filter out notifications on the lockscreen if the lockscreen hosted dream is active. If the user - * stops dreaming, pulls the shade down or unlocks the device, then the notifications are unhidden. - */ -@CoordinatorScope -class DreamCoordinator -@Inject -constructor( - private val statusBarStateController: SysuiStatusBarStateController, - @Application private val scope: CoroutineScope, - private val keyguardRepository: KeyguardRepository, -) : Coordinator { - private var isOnKeyguard = false - private var isLockscreenHostedDream = false - - override fun attach(pipeline: NotifPipeline) { - pipeline.addPreGroupFilter(filter) - statusBarStateController.addCallback(statusBarStateListener) - scope.launch { attachFilterOnDreamingStateChange() } - recordStatusBarState(statusBarStateController.state) - } - - private val filter = - object : NotifFilter("LockscreenHostedDreamFilter") { - var isFiltering = false - override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean { - return isFiltering - } - inline fun update(msg: () -> String) { - val wasFiltering = isFiltering - isFiltering = isLockscreenHostedDream && isOnKeyguard - if (wasFiltering != isFiltering) { - invalidateList(msg()) - } - } - } - - private val statusBarStateListener = - object : StatusBarStateController.StateListener { - override fun onStateChanged(newState: Int) { - recordStatusBarState(newState) - } - } - - private suspend fun attachFilterOnDreamingStateChange() { - keyguardRepository.isActiveDreamLockscreenHosted.collect { isDreaming -> - recordDreamingState(isDreaming) - } - } - - private fun recordStatusBarState(newState: Int) { - isOnKeyguard = newState == StatusBarState.KEYGUARD - filter.update { "recordStatusBarState: " + StatusBarState.toString(newState) } - } - - private fun recordDreamingState(isDreaming: Boolean) { - isLockscreenHostedDream = isDreaming - filter.update { "recordLockscreenHostedDreamState: $isDreaming" } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt index 96c260bb0852..46d4560ff102 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt @@ -15,8 +15,6 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag import com.android.systemui.statusbar.notification.collection.PipelineDumpable @@ -41,7 +39,6 @@ class NotifCoordinatorsImpl @Inject constructor( sectionStyleProvider: SectionStyleProvider, - featureFlags: FeatureFlags, dataStoreCoordinator: DataStoreCoordinator, hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator, hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator, @@ -70,7 +67,6 @@ constructor( visualStabilityCoordinator: VisualStabilityCoordinator, sensitiveContentCoordinator: SensitiveContentCoordinator, dismissibilityCoordinator: DismissibilityCoordinator, - dreamCoordinator: DreamCoordinator, statsLoggerCoordinator: NotificationStatsLoggerCoordinator, bundleCoordinator: BundleCoordinator, ) : NotifCoordinators { @@ -115,10 +111,6 @@ constructor( mCoordinators.add(remoteInputCoordinator) mCoordinators.add(dismissibilityCoordinator) - if (featureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { - mCoordinators.add(dreamCoordinator) - } - if (NotificationsLiveDataStoreRefactor.isEnabled) { mCoordinators.add(statsLoggerCoordinator) } @@ -152,10 +144,7 @@ constructor( sectionStyleProvider.setMinimizedSections(setOf(rankingCoordinator.minimizedSectioner)) if (SortBySectionTimeFlag.isEnabled) { sectionStyleProvider.setSilentSections( - listOf( - rankingCoordinator.silentSectioner, - rankingCoordinator.minimizedSectioner, - ) + listOf(rankingCoordinator.silentSectioner, rankingCoordinator.minimizedSectioner) ) } else { sectionStyleProvider.setSilentSections( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt index 9b382e61b2e3..697a6ce52ba9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.notification.collection.render.NotifStats import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel @@ -26,6 +27,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map @@ -74,6 +76,17 @@ constructor( val allNotificationsCountValue: Int get() = repository.activeNotifications.value.individuals.size + /** The notifications that are promoted and ongoing. Sorted by priority order. */ + val promotedOngoingNotifications: Flow<List<ActiveNotificationModel>> = + if (StatusBarNotifChips.isEnabled) { + // TODO(b/364653005): Filter all the notifications down to just the promoted ones. + // TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow + // instead of being separate. + topLevelRepresentativeNotifications + } else { + flowOf(emptyList()) + } + /** * The priority ongoing call notification, or null if there is no ongoing call. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt index 695e088b5f8b..fbec6406e9d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel import android.content.Context import android.icu.text.MessageFormat -import com.android.app.tracing.coroutines.flow.flowOn import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.modes.shared.ModesUi @@ -40,6 +39,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 560028cb5640..d538f52fd637 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -449,7 +449,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } // We need to reset the View state, even if the animation was cancelled - onAppearAnimationFinished(isAppearing); + onAppearAnimationFinished(isAppearing, /* cancelled = */ !mRunWithoutInterruptions); if (mRunWithoutInterruptions) { InteractionJankMonitor.getInstance().end(getCujType(isAppearing)); @@ -463,6 +463,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView if (onStartedRunnable != null) { onStartedRunnable.run(); } + onAppearAnimationStarted(isAppearing); mRunWithoutInterruptions = true; Configuration.Builder builder = Configuration.Builder .withView(getCujType(isAppearing), ActivatableNotificationView.this); @@ -486,6 +487,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView frameTimeNanos -> { if (mAppearAnimator == cachedAnimator) { mAppearAnimator.start(); + } else { + onAppearAnimationSkipped(isAppearing); } }, delay); } @@ -502,7 +505,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } } - protected void onAppearAnimationFinished(boolean wasAppearing) { + protected void onAppearAnimationStarted(boolean isAppear) { + } + + protected void onAppearAnimationSkipped(boolean isAppear) { + } + + protected void onAppearAnimationFinished(boolean wasAppearing, boolean cancelled) { } private void cancelAppearAnimation() { @@ -830,4 +839,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView }); } } + + protected void dumpAppearAnimationProperties(IndentingPrintWriter pw, String[] args) { + pw.print("AppearAnimation: "); + pw.print("mDrawingAppearAnimation", mDrawingAppearAnimation); + pw.print("mAppearAnimationFraction", mAppearAnimationFraction); + pw.print("mIsHeadsUpAnimation", mIsHeadsUpAnimation); + pw.print("mTargetPoint", mTargetPoint); + pw.println(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 49153d1d8eef..38e66099022c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -1835,6 +1835,25 @@ public class ExpandableNotificationRow extends ActivatableNotificationView void logSkipResetAllContentAlphas( NotificationEntry entry ); + + /** Called when we start an appear animation. */ + void logStartAppearAnimation(NotificationEntry entry, boolean isAppear); + + /** Called when we cancel the running appear animation. */ + void logCancelAppearDrawing(NotificationEntry entry, boolean wasDrawing); + + /** Called when the animator of the appear animation is started. */ + void logAppearAnimationStarted(NotificationEntry entry, boolean isAppear); + + /** Called when we prepared an appear animation, but the animator was never started. */ + void logAppearAnimationSkipped(NotificationEntry entry, boolean isAppear); + + /** Called when the animator of the appear animation is finished. */ + void logAppearAnimationFinished( + NotificationEntry entry, + boolean isAppear, + boolean cancelled + ); } /** @@ -3165,6 +3184,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override + public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear, + Runnable onFinishRunnable) { + mLogger.logStartAppearAnimation(getEntry(), /* isAppear = */ true); + super.performAddAnimation(delay, duration, isHeadsUpAppear, onFinishRunnable); + } + + @Override public long performRemoveAnimation( long duration, long delay, @@ -3173,6 +3199,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView Runnable onStartedRunnable, Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener, ClipSide clipSide) { + mLogger.logStartAppearAnimation(getEntry(), /* isAppear = */ false); if (mMenuRow != null && mMenuRow.isMenuVisible()) { Animator anim = getTranslateViewAnimator(0f, null /* listener */); if (anim != null) { @@ -3201,8 +3228,25 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override - protected void onAppearAnimationFinished(boolean wasAppearing) { - super.onAppearAnimationFinished(wasAppearing); + protected void onAppearAnimationStarted(boolean isAppear) { + mLogger.logAppearAnimationStarted(getEntry(), /* isAppear = */ isAppear); + super.onAppearAnimationStarted(isAppear); + } + + @Override + protected void onAppearAnimationSkipped(boolean isAppear) { + mLogger.logAppearAnimationSkipped(getEntry(), /* isAppear = */ isAppear); + super.onAppearAnimationSkipped(isAppear); + } + + @Override + protected void onAppearAnimationFinished(boolean wasAppearing, boolean cancelled) { + mLogger.logAppearAnimationFinished( + /* entry = */ getEntry(), + /* isAppear = */ wasAppearing, + /* cancelled = */ cancelled + ); + super.onAppearAnimationFinished(wasAppearing, cancelled); if (wasAppearing) { // During the animation the visible view might have changed, so let's make sure all // alphas are reset @@ -3218,6 +3262,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override + public void cancelAppearDrawing() { + mLogger.logCancelAppearDrawing(getEntry(), isDrawingAppearAnimation()); + super.cancelAppearDrawing(); + } + + @Override public void resetAllContentAlphas() { mLogger.logResetAllContentAlphas(getEntry()); mPrivateLayout.setAlpha(1f); @@ -3883,6 +3933,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView dumpHeights(pw); } showingLayout.dump(pw, args); + dumpAppearAnimationProperties(pw, args); dumpCustomOutline(pw, args); dumpClipping(pw, args); if (getViewState() != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index c31a2cb8908b..23a2facf4c5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -209,6 +209,32 @@ public class ExpandableNotificationRowController implements NotifViewController ) { mLogBufferLogger.logSkipResetAllContentAlphas(entry); } + + @Override + public void logStartAppearAnimation(NotificationEntry entry, boolean isAppear) { + mLogBufferLogger.logStartAppearAnimation(entry, isAppear); + } + + @Override + public void logCancelAppearDrawing(NotificationEntry entry, boolean wasDrawing) { + mLogBufferLogger.logCancelAppearDrawing(entry, wasDrawing); + } + + @Override + public void logAppearAnimationStarted(NotificationEntry entry, boolean isAppear) { + mLogBufferLogger.logAppearAnimationStarted(entry, isAppear); + } + + @Override + public void logAppearAnimationSkipped(NotificationEntry entry, boolean isAppear) { + mLogBufferLogger.logAppearAnimationSkipped(entry, isAppear); + } + + @Override + public void logAppearAnimationFinished(NotificationEntry entry, boolean isAppear, + boolean cancelled) { + mLogBufferLogger.logAppearAnimationFinished(entry, isAppear, cancelled); + } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt index b90aa107d617..71f9c07ba2d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row import android.widget.flags.Flags.notifLinearlayoutOptimized import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory import com.android.systemui.statusbar.notification.shared.NotificationViewFlipperPausing import javax.inject.Inject import javax.inject.Provider @@ -35,6 +36,7 @@ constructor( bigPictureLayoutInflaterFactory: BigPictureLayoutInflaterFactory, optimizedLinearLayoutFactory: NotificationOptimizedLinearLayoutFactory, notificationViewFlipperFactory: Provider<NotificationViewFlipperFactory>, + notificationRowIconViewInflaterFactory: NotificationRowIconViewInflaterFactory, ) : NotifRemoteViewsFactoryContainer { override val factories: Set<NotifRemoteViewsFactory> = buildSet { add(precomputedTextViewFactory) @@ -47,5 +49,8 @@ constructor( if (NotificationViewFlipperPausing.isEnabled) { add(notificationViewFlipperFactory.get()) } + if (android.app.Flags.notificationsRedesignAppIcons()) { + add(notificationRowIconViewInflaterFactory) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index e10fd8f3b3fb..41abac1d47f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -342,7 +342,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder * Cancel any pending content view frees from {@link #freeNotificationView} for the provided * content views. * - * @param row top level notification row containing the content views + * @param row top level notification row containing the content views * @param contentViews content views to cancel pending frees on */ private void cancelContentViewFrees( @@ -478,6 +478,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP)); setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC)); + if (android.app.Flags.notificationsRedesignAppIcons()) { + setRemoteViewsInflaterFactory(result.mNewGroupHeaderView, + notifLayoutInflaterFactoryProvider.provide(row, FLAG_GROUP_SUMMARY_HEADER)); + setRemoteViewsInflaterFactory(result.mNewMinimizedGroupHeaderView, + notifLayoutInflaterFactoryProvider.provide(row, + FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER)); + } } private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews, @@ -516,6 +523,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder logger.logAsyncTaskProgress(entry, "contracted view applied"); result.inflatedContentView = v; } + @Override public RemoteViews getRemoteView() { return result.newContentView; @@ -1406,6 +1414,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder @VisibleForTesting abstract static class ApplyCallback { public abstract void setResultView(View v); + public abstract RemoteViews getRemoteView(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt index b1e90329e01a..6e78f561cfc1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt @@ -30,7 +30,7 @@ class NotificationRowLogger @Inject constructor( @NotificationLog private val buffer: LogBuffer, - @NotificationRenderLog private val notificationRenderBuffer: LogBuffer + @NotificationRenderLog private val notificationRenderBuffer: LogBuffer, ) { fun logKeepInParentChildDetached(child: NotificationEntry, oldParent: NotificationEntry?) { buffer.log( @@ -40,7 +40,7 @@ constructor( str1 = child.logKey str2 = oldParent.logKey }, - { "Detach child $str1 kept in parent $str2" } + { "Detach child $str1 kept in parent $str2" }, ) } @@ -52,13 +52,13 @@ constructor( str1 = child.logKey str2 = newParent.logKey }, - { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" } + { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" }, ) } fun logRemoveTransientFromContainer( childEntry: NotificationEntry, - containerEntry: NotificationEntry + containerEntry: NotificationEntry, ) { notificationRenderBuffer.log( TAG, @@ -67,25 +67,20 @@ constructor( str1 = childEntry.logKey str2 = containerEntry.logKey }, - { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" } + { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" }, ) } - fun logRemoveTransientFromNssl( - childEntry: NotificationEntry, - ) { + fun logRemoveTransientFromNssl(childEntry: NotificationEntry) { notificationRenderBuffer.log( TAG, LogLevel.INFO, { str1 = childEntry.logKey }, - { "RemoveTransientRow from Nssl: childKey: $str1" } + { "RemoveTransientRow from Nssl: childKey: $str1" }, ) } - fun logRemoveTransientFromViewGroup( - childEntry: NotificationEntry, - containerView: ViewGroup, - ) { + fun logRemoveTransientFromViewGroup(childEntry: NotificationEntry, containerView: ViewGroup) { notificationRenderBuffer.log( TAG, LogLevel.WARNING, @@ -93,14 +88,14 @@ constructor( str1 = childEntry.logKey str2 = containerView.toString() }, - { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" } + { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" }, ) } fun logAddTransientRow( childEntry: NotificationEntry, containerEntry: NotificationEntry, - index: Int + index: Int, ) { notificationRenderBuffer.log( TAG, @@ -110,14 +105,11 @@ constructor( str2 = containerEntry.logKey int1 = index }, - { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" } + { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" }, ) } - fun logRemoveTransientRow( - childEntry: NotificationEntry, - containerEntry: NotificationEntry, - ) { + fun logRemoveTransientRow(childEntry: NotificationEntry, containerEntry: NotificationEntry) { notificationRenderBuffer.log( TAG, LogLevel.ERROR, @@ -125,7 +117,7 @@ constructor( str1 = childEntry.logKey str2 = containerEntry.logKey }, - { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" } + { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" }, ) } @@ -134,7 +126,7 @@ constructor( TAG, LogLevel.INFO, { str1 = entry.logKey }, - { "resetAllContentAlphas: $str1" } + { "resetAllContentAlphas: $str1" }, ) } @@ -143,7 +135,72 @@ constructor( TAG, LogLevel.INFO, { str1 = entry.logKey }, - { "Skip resetAllContentAlphas: $str1" } + { "Skip resetAllContentAlphas: $str1" }, + ) + } + + fun logStartAppearAnimation(entry: NotificationEntry, isAppear: Boolean) { + notificationRenderBuffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = entry.logKey + bool1 = isAppear + }, + { "startAppearAnimation childKey: $str1 isAppear:$bool1" }, + ) + } + + fun logCancelAppearDrawing(entry: NotificationEntry, wasDrawing: Boolean) { + notificationRenderBuffer.log( + TAG, + LogLevel.WARNING, + { + str1 = entry.logKey + bool1 = wasDrawing + }, + { "cancelAppearDrawing childKey: $str1 wasDrawing:$bool1" }, + ) + } + + fun logAppearAnimationStarted(entry: NotificationEntry, isAppear: Boolean) { + notificationRenderBuffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = entry.logKey + bool1 = isAppear + }, + { "onAppearAnimationStarted childKey: $str1 isAppear:$bool1" }, + ) + } + + fun logAppearAnimationSkipped(entry: NotificationEntry, isAppear: Boolean) { + notificationRenderBuffer.log( + TAG, + LogLevel.WARNING, + { + str1 = entry.logKey + bool1 = isAppear + }, + { "Skipped an appear animation childKey: $str1 isAppear:$bool1" }, + ) + } + + fun logAppearAnimationFinished( + entry: NotificationEntry, + isAppear: Boolean, + cancelled: Boolean, + ) { + notificationRenderBuffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = entry.logKey + bool1 = isAppear + bool2 = cancelled + }, + { "onAppearAnimationFinished childKey: $str1 isAppear:$bool1 cancelled:$bool2" }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java index 84f2f6670839..4feb78a46d60 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java @@ -17,6 +17,8 @@ package com.android.systemui.statusbar.notification.row; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.notification.row.icon.AppIconProviderModule; +import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderModule; import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; import dagger.Binds; @@ -28,7 +30,7 @@ import javax.inject.Provider; /** * Dagger Module containing notification row and view inflation implementations. */ -@Module +@Module(includes = {AppIconProviderModule.class, NotificationIconStyleProviderModule.class}) public abstract class NotificationRowModule { /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt new file mode 100644 index 000000000000..24b5cf1aa33b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.icon + +import android.app.ActivityManager +import android.app.Flags +import android.content.Context +import android.content.pm.PackageManager.NameNotFoundException +import android.graphics.Color +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.util.Log +import com.android.internal.R +import com.android.launcher3.icons.BaseIconFactory +import com.android.systemui.dagger.SysUISingleton +import dagger.Module +import dagger.Provides +import javax.inject.Inject +import javax.inject.Provider + +/** A provider used to cache and fetch app icons used by notifications. */ +interface AppIconProvider { + @Throws(NameNotFoundException::class) + fun getOrFetchAppIcon(packageName: String, context: Context): Drawable +} + +@SysUISingleton +class AppIconProviderImpl @Inject constructor(private val sysuiContext: Context) : AppIconProvider { + private val iconFactory: BaseIconFactory + get() { + val isLowRam = ActivityManager.isLowRamDeviceStatic() + val res = sysuiContext.resources + val iconSize: Int = + res.getDimensionPixelSize( + if (isLowRam) R.dimen.notification_small_icon_size_low_ram + else R.dimen.notification_small_icon_size + ) + return BaseIconFactory(sysuiContext, res.configuration.densityDpi, iconSize) + } + + override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable { + val icon = context.packageManager.getApplicationIcon(packageName) + return BitmapDrawable( + context.resources, + iconFactory.createScaledBitmap(icon, BaseIconFactory.MODE_HARDWARE), + ) + } +} + +class NoOpIconProvider : AppIconProvider { + companion object { + const val TAG = "NoOpIconProvider" + } + + override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable { + Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.") + return ColorDrawable(Color.WHITE) + } +} + +@Module +class AppIconProviderModule { + @Provides + @SysUISingleton + fun provideImpl(realImpl: Provider<AppIconProviderImpl>): AppIconProvider = + if (Flags.notificationsRedesignAppIcons()) { + realImpl.get() + } else { + NoOpIconProvider() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt new file mode 100644 index 000000000000..165c1a7803a9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.icon + +import android.annotation.WorkerThread +import android.app.Flags +import android.content.Context +import android.content.pm.ApplicationInfo +import android.service.notification.StatusBarNotification +import android.util.Log +import com.android.systemui.dagger.SysUISingleton +import dagger.Module +import dagger.Provides +import javax.inject.Inject +import javax.inject.Provider + +/** + * A provider used to cache and fetch information about which icon should be displayed by + * notifications. + */ +interface NotificationIconStyleProvider { + @WorkerThread + fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean +} + +@SysUISingleton +class NotificationIconStyleProviderImpl @Inject constructor() : NotificationIconStyleProvider { + override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean { + val packageContext = notification.getPackageContext(context) + return !belongsToHeadlessSystemApp(packageContext) + } + + @WorkerThread + private fun belongsToHeadlessSystemApp(context: Context): Boolean { + val info = context.applicationInfo + if (info != null) { + if ((info.flags and ApplicationInfo.FLAG_SYSTEM) == 0) { + // It's not a system app at all. + return false + } else { + // If there's no launch intent, it's probably a headless app. + val pm = context.packageManager + return (pm.getLaunchIntentForPackage(info.packageName) == null) + } + } else { + // If for some reason we don't have the app info, we don't know; best assume it's + // not a system app. + return false + } + } +} + +class NoOpIconStyleProvider : NotificationIconStyleProvider { + companion object { + const val TAG = "NoOpIconStyleProvider" + } + + override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean { + Log.wtf(TAG, "NoOpIconStyleProvider should not be used anywhere.") + return true + } +} + +@Module +class NotificationIconStyleProviderModule { + @Provides + @SysUISingleton + fun provideImpl( + realImpl: Provider<NotificationIconStyleProviderImpl> + ): NotificationIconStyleProvider = + if (Flags.notificationsRedesignAppIcons()) { + realImpl.get() + } else { + NoOpIconStyleProvider() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt new file mode 100644 index 000000000000..79defd255de0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.icon + +import android.content.Context +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.View +import com.android.internal.widget.NotificationRowIconView +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.row.NotifRemoteViewsFactory +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder +import javax.inject.Inject + +/** + * A factory which owns the construction of any NotificationRowIconView inside of Notifications in + * SystemUI. This allows overriding the small icon with the app icon in notifications. + */ +class NotificationRowIconViewInflaterFactory +@Inject +constructor( + private val appIconProvider: AppIconProvider, + private val iconStyleProvider: NotificationIconStyleProvider, +) : NotifRemoteViewsFactory { + override fun instantiate( + row: ExpandableNotificationRow, + @NotificationRowContentBinder.InflationFlag layoutType: Int, + parent: View?, + name: String, + context: Context, + attrs: AttributeSet, + ): View? { + return when (name) { + NotificationRowIconView::class.java.name -> + NotificationRowIconView(context, attrs).also { view -> + val sbn = row.entry.sbn + view.setIconProvider( + object : NotificationRowIconView.NotificationIconProvider { + override fun shouldShowAppIcon(): Boolean { + return iconStyleProvider.shouldShowAppIcon(row.entry.sbn, context) + } + + override fun getAppIcon(): Drawable { + return appIconProvider.getOrFetchAppIcon(sbn.packageName, context) + } + } + ) + } + else -> null + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index d828a6700ee6..ec1dc0a77118 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -25,6 +25,8 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_A import static com.android.systemui.Flags.notificationOverExpansionClippingFix; import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE; +import static com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent.SCROLL_DOWN; +import static com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent.SCROLL_UP; import static com.android.systemui.util.DumpUtilsKt.println; import static com.android.systemui.util.DumpUtilsKt.visibilityString; @@ -118,8 +120,10 @@ import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCyc import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation; import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; +import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent; import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds; import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape; +import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState; import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; @@ -609,7 +613,7 @@ public class NotificationStackScrollLayout @Override public boolean isScrolledToTop() { if (SceneContainerFlag.isEnabled()) { - return mScrollViewFields.isScrolledToTop(); + return mScrollViewFields.getScrollState().isScrolledToTop(); } else { return mOwnScrollY == 0; } @@ -1247,9 +1251,25 @@ public class NotificationStackScrollLayout } @Override - public void setScrolledToTop(boolean scrolledToTop) { - if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; - mScrollViewFields.setScrolledToTop(scrolledToTop); + public void setScrollState(@NonNull ShadeScrollState scrollState) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { + return; + } + + boolean forwardScrollable = + scrollState.getScrollPosition() < scrollState.getMaxScrollPosition(); + boolean backwardScrollable = scrollState.getScrollPosition() > 0; + mScrollable = forwardScrollable || backwardScrollable; + mForwardScrollable = forwardScrollable; + mBackwardScrollable = backwardScrollable; + + boolean scrollPositionChanged = mScrollViewFields.getScrollState().getScrollPosition() + != scrollState.getScrollPosition(); + mScrollViewFields.setScrollState(scrollState); + + if (scrollPositionChanged) { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED); + } } @Override @@ -1295,6 +1315,12 @@ public class NotificationStackScrollLayout } @Override + public void setAccessibilityScrollEventConsumer( + @Nullable Consumer<AccessibilityScrollEvent> consumer) { + mScrollViewFields.setAccessibilityScrollEventConsumer(consumer); + } + + @Override public void setCurrentGestureOverscrollConsumer(@Nullable Consumer<Boolean> consumer) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; mScrollViewFields.setCurrentGestureOverscrollConsumer(consumer); @@ -2645,6 +2671,11 @@ public class NotificationStackScrollLayout return mHeadsUpInset; } + @Override + public int getStackBottomInset() { + return mPaddingBetweenElements + mShelf.getIntrinsicHeight(); + } + /** * Calculate the gap height between two different views * @@ -4243,17 +4274,27 @@ public class NotificationStackScrollLayout */ @Override public boolean performAccessibilityActionInternal(int action, Bundle arguments) { - // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled. - if (SceneContainerFlag.isEnabled()) { - return super.performAccessibilityActionInternal(action, arguments); - } - if (super.performAccessibilityActionInternal(action, arguments)) { return true; } if (!isEnabled()) { return false; } + + if (SceneContainerFlag.isEnabled()) { + switch (action) { + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: + case android.R.id.accessibilityActionScrollDown: + mScrollViewFields.sendAccessibilityScrollEvent(SCROLL_DOWN); + return true; + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: + case android.R.id.accessibilityActionScrollUp: + mScrollViewFields.sendAccessibilityScrollEvent(SCROLL_UP); + return true; + } + return false; + } + int direction = -1; switch (action) { case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: @@ -5029,25 +5070,21 @@ public class NotificationStackScrollLayout @Override public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { super.onInitializeAccessibilityEventInternal(event); - // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled. - if (SceneContainerFlag.isEnabled()) { - return; - } - event.setScrollable(mScrollable); event.setMaxScrollX(mScrollX); - event.setScrollY(mOwnScrollY); - event.setMaxScrollY(getScrollRange()); + + if (SceneContainerFlag.isEnabled()) { + event.setScrollY(mScrollViewFields.getScrollState().getScrollPosition()); + event.setMaxScrollY(mScrollViewFields.getScrollState().getMaxScrollPosition()); + } else { + event.setScrollY(mOwnScrollY); + event.setMaxScrollY(getScrollRange()); + } } @Override public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); - // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled. - if (SceneContainerFlag.isEnabled()) { - return; - } - if (mScrollable) { info.setScrollable(true); if (mBackwardScrollable) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 00c5c40fc8ac..04974b4353f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -24,6 +24,7 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_ import static com.android.server.notification.Flags.screenshareNotificationHiding; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.Flags.confineNotificationTouchToViewWidth; +import static com.android.systemui.Flags.ignoreTouchesNextToNotificationShelf; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener; @@ -607,6 +608,16 @@ public class NotificationStackScrollLayoutController implements Dumpable { true /* requireMinHeight */, false /* ignoreDecors */, !confineNotificationTouchToViewWidth() /* ignoreWidth */); + + // Verify the MotionEvent x,y are actually inside the touch area of the shelf, + // since the shelf may be animated down to a collapsed size on keyguard. + if (ignoreTouchesNextToNotificationShelf()) { + if (child instanceof NotificationShelf shelf) { + if (!NotificationSwipeHelper.isTouchInView(ev, shelf)) { + return null; + } + } + } if (child instanceof ExpandableNotificationRow row) { ExpandableNotificationRow parent = row.getNotificationParent(); if (parent != null && parent.areChildrenExpanded() @@ -2122,9 +2133,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { boolean hunWantsIt = false; if (shouldHeadsUpHandleTouch()) { hunWantsIt = mHeadsUpTouchHelper.onInterceptTouchEvent(ev); - if (hunWantsIt) { - mView.startDraggingOnHun(); - } } boolean swipeWantsIt = false; if (mLongPressedView == null && !mView.isBeingDragged() @@ -2210,6 +2218,9 @@ public class NotificationStackScrollLayoutController implements Dumpable { boolean hunWantsIt = false; if (shouldHeadsUpHandleTouch()) { hunWantsIt = mHeadsUpTouchHelper.onTouchEvent(ev); + if (hunWantsIt) { + mView.startDraggingOnHun(); + } } // Check if we need to clear any snooze leavebehinds diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index 7e327e66982c..0e94ca351835 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE; +import static com.android.systemui.Flags.ignoreTouchesNextToNotificationShelf; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -39,6 +40,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -503,13 +505,21 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc final int height = (view instanceof ExpandableView) ? ((ExpandableView) view).getActualHeight() : view.getHeight(); + final int width; + if (ignoreTouchesNextToNotificationShelf()) { + width = (view instanceof NotificationShelf) + ? ((NotificationShelf) view).getActualWidth() + : view.getWidth(); + } else { + width = view.getWidth(); + } final int rx = (int) ev.getRawX(); final int ry = (int) ev.getRawY(); int[] temp = new int[2]; view.getLocationOnScreen(temp); final int x = temp[0]; final int y = temp[1]; - Rect rect = new Rect(x, y, x + view.getWidth(), y + height); + Rect rect = new Rect(x, y, x + width, y + height); boolean ret = rect.contains(rx, ry); return ret; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt index f6e8b8f0166b..cf6d45a8938e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.notification.stack import android.util.IndentingPrintWriter +import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape +import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import com.android.systemui.util.printSection import com.android.systemui.util.println import java.util.function.Consumer @@ -32,8 +34,9 @@ import java.util.function.Consumer class ScrollViewFields { /** Used to produce the clipping path */ var scrimClippingShape: ShadeScrimShape? = null - /** Whether the notifications are scrolled all the way to the top (i.e. when freshly opened) */ - var isScrolledToTop: Boolean = true + + /** Scroll state of the notification shade. */ + var scrollState: ShadeScrollState = ShadeScrollState() /** * Height in view pixels at which the Notification Stack would like to be laid out, including @@ -47,6 +50,13 @@ class ScrollViewFields { * placeholder */ var syntheticScrollConsumer: Consumer<Float>? = null + + /** + * When the NSSL navigates through the notifications with TalkBack, it can send scroll events + * here, to be able to browse through the whole list of notifications in the shade. + */ + var accessibilityScrollEventConsumer: Consumer<AccessibilityScrollEvent>? = null + /** * When a gesture is consumed internally by NSSL but needs to be handled by other elements (such * as the notif scrim) as overscroll, we can notify the placeholder through here. @@ -86,10 +96,15 @@ class ScrollViewFields { fun sendRemoteInputRowBottomBound(bottomY: Float?) = remoteInputRowBottomBoundConsumer?.accept(bottomY) + /** send an [AccessibilityScrollEvent] to the [accessibilityScrollEventConsumer] if present */ + fun sendAccessibilityScrollEvent(event: AccessibilityScrollEvent) { + accessibilityScrollEventConsumer?.accept(event) + } + fun dump(pw: IndentingPrintWriter) { pw.printSection("StackViewStates") { pw.println("scrimClippingShape", scrimClippingShape) - pw.println("isScrolledToTop", isScrolledToTop) + pw.println("scrollState", scrollState) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt index c0f1a5619140..5ec4c8988697 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt @@ -17,7 +17,10 @@ package com.android.systemui.statusbar.notification.stack.data.repository import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds +import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState +import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow @@ -44,9 +47,9 @@ class NotificationPlaceholderRepository @Inject constructor() { /** height made available to the notifications in the size-constrained mode of lock screen. */ val constrainedAvailableSpace = MutableStateFlow(0) - /** - * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any - * further. - */ - val scrolledToTop = MutableStateFlow(true) + /** Scroll state of the notification shade. */ + val shadeScrollState = MutableStateFlow(ShadeScrollState()) + + /** A consumer of [AccessibilityScrollEvent]s. */ + var accessibilityScrollEventConsumer: Consumer<AccessibilityScrollEvent>? = null } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt index 32e092bcdf4d..d4dd1d4b9e0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt @@ -23,8 +23,11 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.data.repository.NotificationPlaceholderRepository import com.android.systemui.statusbar.notification.stack.data.repository.NotificationViewHeightRepository +import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding +import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState +import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -78,11 +81,9 @@ constructor( val constrainedAvailableSpace: StateFlow<Int> = placeholderRepository.constrainedAvailableSpace.asStateFlow() - /** - * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any - * further. - */ - val scrolledToTop: StateFlow<Boolean> = placeholderRepository.scrolledToTop.asStateFlow() + /** Scroll state of the notification shade. */ + val shadeScrollState: StateFlow<ShadeScrollState> = + placeholderRepository.shadeScrollState.asStateFlow() /** * The amount in px that the notification stack should scroll due to internal expansion. This @@ -123,9 +124,9 @@ constructor( placeholderRepository.shadeScrimBounds.value = bounds } - /** Sets whether the notification stack is scrolled to the top. */ - fun setScrolledToTop(scrolledToTop: Boolean) { - placeholderRepository.scrolledToTop.value = scrolledToTop + /** Updates the current scroll state of the notification shade. */ + fun setScrollState(shadeScrollState: ShadeScrollState) { + placeholderRepository.shadeScrollState.value = shadeScrollState } /** Sets the amount (px) that the notification stack should scroll due to internal expansion. */ @@ -133,6 +134,16 @@ constructor( viewHeightRepository.syntheticScroll.value = delta } + /** Sends an [AccessibilityScrollEvent] to scroll the stack up or down. */ + fun sendAccessibilityScrollEvent(accessibilityScrollEvent: AccessibilityScrollEvent) { + placeholderRepository.accessibilityScrollEventConsumer?.accept(accessibilityScrollEvent) + } + + /** Set a consumer for the [AccessibilityScrollEvent]s to be handled by the placeholder. */ + fun setAccessibilityScrollEventConsumer(consumer: Consumer<AccessibilityScrollEvent>?) { + placeholderRepository.accessibilityScrollEventConsumer = consumer + } + /** Sets whether the current touch gesture is overscroll. */ fun setCurrentGestureOverscroll(isOverscroll: Boolean) { viewHeightRepository.isCurrentGestureOverscroll.value = isOverscroll diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/AccessibilityScrollEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/AccessibilityScrollEvent.kt new file mode 100644 index 000000000000..01341e1d056d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/AccessibilityScrollEvent.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack.shared.model + +/** + * An event to be sent by the NotificationStackScrollLayout to the NotificationsPlaceholder, when + * TalkBack runs out of visible notifications, and wants to scroll the shade to access more. + */ +enum class AccessibilityScrollEvent { + SCROLL_UP, + SCROLL_DOWN, +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrollState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrollState.kt new file mode 100644 index 000000000000..3963286094f8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrollState.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack.shared.model + +data class ShadeScrollState( + /** + * Whether the notification stack is scrolled to the top (i.e. when freshly opened). It also + * returns true, when scrolling is not possible because all the content fits in the current + * viewport. + */ + val isScrolledToTop: Boolean = true, + + /** + * Current scroll position of the shade. 0 when scrolled to the top, [maxScrollPosition] when + * scrolled all the way to the bottom. + */ + val scrollPosition: Int = 0, + + /** + * Max scroll position of the shade. 0, when no scrolling is possible e.g. all the content fits + * in the current viewport. + */ + val maxScrollPosition: Int = 0, +) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt index 6ad9f01ca4ff..5249a6d304ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.notification.stack.ui.view import android.view.View +import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape +import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import java.util.function.Consumer /** @@ -35,6 +37,9 @@ interface NotificationScrollView { /** Height in pixels required to display the top HeadsUp Notification. */ val topHeadsUpHeight: Int + /** Bottom inset of the Notification Stack that us used to display the Shelf. */ + val stackBottomInset: Int + /** * Since this is an interface rather than a literal View, this provides cast-like access to the * underlying view. @@ -62,12 +67,15 @@ interface NotificationScrollView { /** set the bottom-most y position in px, where we can draw HUNs in this view's coordinates */ fun setHeadsUpBottom(headsUpBottom: Float) - /** set whether the view has been scrolled all the way to the top */ - fun setScrolledToTop(scrolledToTop: Boolean) + /** Updates the current scroll state of the notification shade. */ + fun setScrollState(scrollState: ShadeScrollState) /** Set a consumer for synthetic scroll events */ fun setSyntheticScrollConsumer(consumer: Consumer<Float>?) + /** Set a consumer for accessibility actions to be handled by the placeholder. */ + fun setAccessibilityScrollEventConsumer(consumer: Consumer<AccessibilityScrollEvent>?) + /** Set a consumer for current gesture overscroll events */ fun setCurrentGestureOverscrollConsumer(consumer: Consumer<Boolean>?) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt index 87d70ba12012..4a768714b84f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt @@ -17,7 +17,8 @@ package com.android.systemui.statusbar.notification.stack.ui.viewbinder import android.util.Log -import com.android.app.tracing.coroutines.flow.filter +import com.android.app.tracing.coroutines.flow.collectTraced +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.view.onLayoutChanged import com.android.systemui.dagger.SysUISingleton @@ -37,7 +38,6 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch /** Binds the [NotificationScrollView]. */ @SysUISingleton @@ -79,39 +79,47 @@ constructor( launch { viewModel .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset) - .collect { view.setScrimClippingShape(it) } + .collectTraced { view.setScrimClippingShape(it) } } - launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } } - launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } } + launch { viewModel.maxAlpha.collectTraced { view.setMaxAlpha(it) } } + launch { viewModel.shadeScrollState.collect { view.setScrollState(it) } } launch { - viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } + viewModel.expandFraction.collectTraced { + view.setExpandFraction(it.coerceIn(0f, 1f)) + } } - launch { viewModel.qsExpandFraction.collect { view.setQsExpandFraction(it) } } + launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } } launch { - viewModel.isShowingStackOnLockscreen.collect { + viewModel.isShowingStackOnLockscreen.collectTraced { view.setShowingStackOnLockscreen(it) } } launch { - viewModel.alphaForLockscreenFadeIn.collect { view.setAlphaForLockscreenFadeIn(it) } + viewModel.alphaForLockscreenFadeIn.collectTraced { + view.setAlphaForLockscreenFadeIn(it) + } } - launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } } - launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } } + launch { viewModel.isScrollable.collectTraced { view.setScrollingEnabled(it) } } + launch { viewModel.isDozing.collectTraced { isDozing -> view.setDozing(isDozing) } } launch { - viewModel.isPulsing.collect { isPulsing -> + viewModel.isPulsing.collectTraced { isPulsing -> view.setPulsing(isPulsing, viewModel.shouldAnimatePulse.value) } } launch { viewModel.shouldResetStackTop .filter { it } - .collect { view.setStackTop(-(view.getHeadsUpInset().toFloat())) } + .collectTraced { view.setStackTop(-(view.getHeadsUpInset().toFloat())) } + } + launch { + viewModel.shouldCloseGuts + .filter { it } + .collectTraced { view.closeGutsOnSceneTouch() } } launch { - viewModel.shouldCloseGuts.filter { it }.collect { view.closeGutsOnSceneTouch() } + viewModel.suppressHeightUpdates.collectTraced { view.suppressHeightUpdates(it) } } - launch { viewModel.suppressHeightUpdates.collect { view.suppressHeightUpdates(it) } } launchAndDispose { view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer) @@ -120,11 +128,13 @@ constructor( view.setRemoteInputRowBottomBoundConsumer( viewModel.remoteInputRowBottomBoundConsumer ) + view.setAccessibilityScrollEventConsumer(viewModel.accessibilityScrollEventConsumer) DisposableHandle { view.setSyntheticScrollConsumer(null) view.setCurrentGestureOverscrollConsumer(null) view.setCurrentGestureInGutsConsumer(null) view.setRemoteInputRowBottomBoundConsumer(null) + view.setAccessibilityScrollEventConsumer(null) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt index aec81b0241a2..574ca3ffe5b6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt @@ -34,8 +34,10 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor +import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimClipping import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape +import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_DELAYED_STACK_FADE_IN import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA import com.android.systemui.util.kotlin.ActivatableFlowDumper @@ -255,16 +257,16 @@ constructor( val maxAlpha: Flow<Float> = stackAppearanceInteractor.alphaForBrightnessMirror.dumpValue("maxAlpha") - /** - * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any - * further. - */ - val scrolledToTop: Flow<Boolean> = - stackAppearanceInteractor.scrolledToTop.dumpValue("scrolledToTop") + /** Scroll state of the notification shade. */ + val shadeScrollState: Flow<ShadeScrollState> = stackAppearanceInteractor.shadeScrollState /** Receives the amount (px) that the stack should scroll due to internal expansion. */ val syntheticScrollConsumer: (Float) -> Unit = stackAppearanceInteractor::setSyntheticScroll + /** Receives an event to scroll the stack up or down. */ + val accessibilityScrollEventConsumer: (AccessibilityScrollEvent) -> Unit = + stackAppearanceInteractor::sendAccessibilityScrollEvent + /** * Receives whether the current touch gesture is overscroll as it has already been consumed by * the stack. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt index c8e83581e831..a8ce47cf2dd3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt @@ -27,12 +27,15 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor +import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding +import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState import com.android.systemui.util.kotlin.ActivatableFlowDumper import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import java.util.function.Consumer import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter @@ -140,9 +143,9 @@ constructor( /** The bottom bound of the currently focused remote input notification row. */ val remoteInputRowBottomBound = remoteInputInteractor.remoteInputRowBottomBound - /** Sets whether the notification stack is scrolled to the top. */ - fun setScrolledToTop(scrolledToTop: Boolean) { - interactor.setScrolledToTop(scrolledToTop) + /** Updates the current scroll state of the notification shade. */ + fun setScrollState(scrollState: ShadeScrollState) { + interactor.setScrollState(scrollState) } /** Sets whether the heads up notification is animating away. */ @@ -155,6 +158,11 @@ constructor( headsUpNotificationInteractor.snooze() } + /** Set a consumer for accessibility events to be handled by the placeholder. */ + fun setAccessibilityScrollEventConsumer(consumer: Consumer<AccessibilityScrollEvent>?) { + interactor.setAccessibilityScrollEventConsumer(consumer) + } + @AssistedFactory interface Factory { fun create(): NotificationsPlaceholderViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index f39af18afcea..878ae91391e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import androidx.annotation.VisibleForTesting +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.dagger.SysUISingleton @@ -158,6 +159,7 @@ constructor( ) { shadeExpansion, qsExpansion -> shadeExpansion || qsExpansion } + .flowName("isAnyExpanded") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -175,6 +177,7 @@ constructor( isAnyExpanded -> isShadeLocked && isAnyExpanded } + .flowName("isShadeLocked") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -227,6 +230,7 @@ constructor( ), keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f }, ) + .flowName("isOnLockscreen") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -239,6 +243,7 @@ constructor( combine(isOnLockscreen, isAnyExpanded) { isKeyguard, isAnyExpanded -> isKeyguard && !isAnyExpanded } + .flowName("isOnLockscreenWithoutShade") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -274,6 +279,7 @@ constructor( combine(isOnGlanceableHub, isAnyExpanded) { isGlanceableHub, isAnyExpanded -> isGlanceableHub && !isAnyExpanded } + .flowName("isOnGlanceableHubWithoutShade") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -288,6 +294,7 @@ constructor( isAnyExpanded -> isDreaming && !isAnyExpanded } + .flowName("isDreamingWithoutShade") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -345,6 +352,7 @@ constructor( } } } + .flowName("shadeCollapseFadeIn") .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), @@ -382,6 +390,7 @@ constructor( bounds.copy(top = top, isAnimated = animate) } } + .flowName("bounds") .stateIn( scope = applicationScope, started = SharingStarted.Lazily, @@ -495,6 +504,7 @@ constructor( // flatMapLatest below, the last value gets emitted, to avoid the randomness of `merge`. val alphaForTransitionsAndShade = merge(alphaForTransitions(viewState), alphaForShadeAndQsExpansion) + .flowName("alphaForTransitionsAndShade") .stateIn( // Use view-level scope instead of ApplicationScope, to prevent collection that // never stops diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index cb4454d88b2d..afa5a7802559 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -128,7 +128,6 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.emergency.EmergencyGesture; import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory; import com.android.systemui.flags.FeatureFlags; @@ -141,8 +140,6 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.MigrateClocksToBlueprint; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder; -import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.views.NavigationBarView; import com.android.systemui.notetask.NoteTaskController; @@ -426,7 +423,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final NotificationsController mNotificationsController; private final StatusBarSignalPolicy mStatusBarSignalPolicy; private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; - private final Lazy<LightRevealScrimViewModel> mLightRevealScrimViewModelLazy; /** Controller for the Shade. */ private final ShadeSurface mShadeSurface; @@ -699,7 +695,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { WiredChargingRippleController wiredChargingRippleController, IDreamManager dreamManager, Lazy<CameraLauncher> cameraLauncherLazy, - Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy, LightRevealScrim lightRevealScrim, AlternateBouncerInteractor alternateBouncerInteractor, UserTracker userTracker, @@ -844,7 +839,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mDeviceStateManager = deviceStateManager; wiredChargingRippleController.registerCallbacks(); - mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy; mLightRevealScrim = lightRevealScrim; mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; @@ -1008,7 +1002,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mStatusBarKeyguardViewManager, getNotificationShadeWindowViewController(), mAmbientIndicationContainer); - updateLightRevealScrimVisibility(); mConfigurationController.addCallback(mConfigurationListener); @@ -1280,11 +1273,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { }); mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront); - if (lightRevealMigration()) { - LightRevealScrimViewBinder.bind( - mLightRevealScrim, mLightRevealScrimViewModelLazy.get()); - } - mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> { Runnable updateOpaqueness = () -> { mNotificationShadeWindowController.setLightRevealScrimOpaque( @@ -1302,7 +1290,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { }); mScreenOffAnimationController.initialize(this, mShadeSurface, mLightRevealScrim); - updateLightRevealScrimVisibility(); if (!SceneContainerFlag.isEnabled()) { mShadeSurface.initDependencies( @@ -2825,23 +2812,13 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mScrimController.setExpansionAffectsAlpha(!unlocking); if (mAlternateBouncerInteractor.isVisibleState()) { - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) - && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED - || mTransitionToFullShadeProgress > 0f)) { - // Assume scrim state for shade is already correct and do nothing - } else { - // Safeguard which prevents the scrim from being stuck in the wrong state - mScrimController.legacyTransitionTo(ScrimState.KEYGUARD); - } + if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) + && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED + || mTransitionToFullShadeProgress > 0f)) { + // Assume scrim state for shade is already correct and do nothing } else { - if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) - && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED - || mTransitionToFullShadeProgress > 0f)) { - mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED_SHADE); - } else { - mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED); - } + // Safeguard which prevents the scrim from being stuck in the wrong state + mScrimController.legacyTransitionTo(ScrimState.KEYGUARD); } // This will cancel the keyguardFadingAway animation if it is running. We need to do // this as otherwise it can remain pending and leave keyguard in a weird state. @@ -2887,7 +2864,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } else { mScrimController.legacyTransitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } - updateLightRevealScrimVisibility(); Trace.endSection(); } @@ -3009,17 +2985,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { return mStatusBarModeRepository.getDefaultDisplay().isTransientShown().getValue(); } - private void updateLightRevealScrimVisibility() { - if (mLightRevealScrim == null) { - // status bar may not be inflated yet - return; - } - - if (!lightRevealMigration()) { - mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha()); - } - } - private final KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @Override @@ -3168,12 +3133,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { public void onDozeAmountChanged(float linear, float eased) { if (!lightRevealMigration() && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { - if (DeviceEntryUdfpsRefactor.isEnabled()) { - // If wakeAndUnlocking, this is handled in AuthRippleInteractor - if (!mBiometricUnlockController.isWakeAndUnlock()) { - mLightRevealScrim.setRevealAmount(1f - linear); - } - } else { + // If wakeAndUnlocking, this is handled in AuthRippleInteractor + if (!mBiometricUnlockController.isWakeAndUnlock()) { mLightRevealScrim.setRevealAmount(1f - linear); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt index bb5aa23fee28..a8c823c35213 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt @@ -20,6 +20,7 @@ import android.content.res.Configuration import android.graphics.Rect import android.os.LocaleList import android.view.View.LAYOUT_DIRECTION_RTL +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener import dagger.assisted.Assisted @@ -30,7 +31,7 @@ class ConfigurationControllerImpl @AssistedInject constructor( @Assisted private val context: Context, -) : ConfigurationController { +) : ConfigurationController, StatusBarConfigurationController { private val listeners: MutableList<ConfigurationListener> = ArrayList() private val lastConfig = Configuration() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt index db237e89a683..5273702cf5e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt @@ -17,7 +17,10 @@ package com.android.systemui.statusbar.phone import android.content.Context import android.hardware.devicestate.DeviceState +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback +import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags import com.android.internal.R /** @@ -47,12 +50,21 @@ internal class FoldStateListener( private var wasFolded: Boolean? = null override fun onDeviceStateChanged(state: DeviceState) { - val isFolded = foldedDeviceStates.contains(state.identifier) + val isFolded: Boolean + val willGoToSleep: Boolean + + if (DeviceStateManagerFlags.deviceStatePropertyMigration()) { + isFolded = state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY) + willGoToSleep = state.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP) + } else { + isFolded = foldedDeviceStates.contains(state.identifier) + willGoToSleep = goToSleepDeviceStates.contains(state.identifier) + } + if (wasFolded == isFolded) { return } wasFolded = isFolded - val willGoToSleep = goToSleepDeviceStates.contains(state.identifier) listener.onFoldStateChanged(isFolded, willGoToSleep) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 8f94c0656836..d0f4b6f4a4bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.OPERATOR_NAME_FRAME_VIEW; +import static com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.OPERATOR_NAME_FRAME_VIEW; import android.graphics.Rect; import android.util.MathUtils; @@ -44,7 +44,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope; +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -63,7 +63,7 @@ import javax.inject.Named; * Controls the appearance of heads up notifications in the icon area and the header itself. * It also controls the roundness of the heads up notifications and the pulsing notifications. */ -@StatusBarFragmentScope +@HomeStatusBarScope public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView> implements OnHeadsUpChangedListener, DarkIconDispatcher.DarkReceiver, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index 316e1f13bc2b..a34ac2e11c2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -22,7 +22,7 @@ import android.content.res.Resources import android.hardware.biometrics.BiometricSourceType import android.provider.Settings import com.android.app.tracing.ListenersTracing.forEachTraced -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java index 7c871e183740..5acc3a684515 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.phone; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; -import static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.LIGHTS_OUT_NOTIF_VIEW; +import static com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.LIGHTS_OUT_NOTIF_VIEW; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -37,7 +37,7 @@ import com.android.internal.view.AppearanceRegion; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore; import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope; +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope; import com.android.systemui.util.ViewController; import javax.inject.Inject; @@ -51,7 +51,7 @@ import javax.inject.Named; * This controller shows and hides the notification dot in the status bar to indicate * whether there are notifications when the device is in {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}. */ -@StatusBarFragmentScope +@HomeStatusBarScope public class LegacyLightsOutNotifController extends ViewController<View> { private final CommandQueue mCommandQueue; private final NotifLiveDataStore mNotifDataStore; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 069c6241491c..dc4d66d1ef5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -29,7 +29,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.IntDef; -import android.app.AlarmManager; import android.graphics.Color; import android.os.Handler; import android.os.Trace; @@ -53,8 +52,6 @@ import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; -import com.android.systemui.CoreStartable; -import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; @@ -81,11 +78,9 @@ import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.util.wakelock.WakeLock; -import com.android.systemui.wallpapers.data.repository.WallpaperRepository; import kotlinx.coroutines.CoroutineDispatcher; import kotlinx.coroutines.ExperimentalCoroutinesApi; @@ -104,8 +99,7 @@ import javax.inject.Inject; */ @SysUISingleton @ExperimentalCoroutinesApi -public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable, - CoreStartable { +public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable { static final String TAG = "ScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -217,7 +211,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final DozeParameters mDozeParameters; private final DockManager mDockManager; - private final AlarmTimeout mTimeTicker; private final KeyguardVisibilityCallback mKeyguardVisibilityCallback; private final Handler mHandler; private final Executor mMainExecutor; @@ -232,7 +225,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private float mAdditionalScrimBehindAlphaKeyguard = 0f; // Combined scrim behind keyguard alpha of default scrim + additional scrim - // (if wallpaper dimming is applied). private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA; private final float mDefaultScrimAlpha; @@ -261,7 +253,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private int mBehindTint; private int mNotificationsTint; - private boolean mWallpaperVisibilityTimedOut; private int mScrimsVisibility; private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener; private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator; @@ -269,7 +260,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private boolean mBlankScreen; private boolean mScreenBlankingCallbackCalled; private Callback mCallback; - private boolean mWallpaperSupportsAmbientMode; private boolean mScreenOn; private boolean mTransparentScrimBackground; @@ -282,7 +272,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private boolean mKeyguardOccluded; private KeyguardTransitionInteractor mKeyguardTransitionInteractor; - private final WallpaperRepository mWallpaperRepository; private CoroutineDispatcher mMainDispatcher; private boolean mIsBouncerToGoneTransitionRunning = false; private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; @@ -332,7 +321,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump public ScrimController( LightBarController lightBarController, DozeParameters dozeParameters, - AlarmManager alarmManager, KeyguardStateController keyguardStateController, DelayedWakeLock.Factory delayedWakeLockFactory, Handler handler, @@ -348,7 +336,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump AlternateBouncerToGoneTransitionViewModel alternateBouncerToGoneTransitionViewModel, KeyguardTransitionInteractor keyguardTransitionInteractor, KeyguardInteractor keyguardInteractor, - WallpaperRepository wallpaperRepository, @Main CoroutineDispatcher mainDispatcher, LargeScreenShadeInterpolator largeScreenShadeInterpolator) { mScrimStateListener = lightBarController::setScrimState; @@ -363,8 +350,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mMainExecutor = mainExecutor; mJavaAdapter = javaAdapter; mScreenOffAnimationController = screenOffAnimationController; - mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout, - "hide_aod_wallpaper", mHandler); mWakeLock = delayedWakeLockFactory.create("Scrims"); // Scrim alpha is initially set to the value on the resource but might be changed // to make sure that text on top of it is legible. @@ -395,17 +380,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mAlternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel; mKeyguardTransitionInteractor = keyguardTransitionInteractor; mKeyguardInteractor = keyguardInteractor; - mWallpaperRepository = wallpaperRepository; mMainDispatcher = mainDispatcher; } - @Override - public void start() { - mJavaAdapter.alwaysCollectFlow( - mWallpaperRepository.getWallpaperSupportsAmbientMode(), - this::setWallpaperSupportsAmbientMode); - } - /** * Attach the controller to the supplied views. */ @@ -627,18 +604,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump holdWakeLock(); } - // AOD wallpapers should fade away after a while. - // Docking pulses may take a long time, wallpapers should also fade away after a while. - mWallpaperVisibilityTimedOut = false; - if (shouldFadeAwayWallpaper()) { - DejankUtils.postAfterTraversal(() -> { - mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(), - AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); - }); - } else { - DejankUtils.postAfterTraversal(mTimeTicker::cancel); - } - if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) { mAnimationDelay = CentralSurfaces.FADE_KEYGUARD_START_DELAY; scheduleUpdate(); @@ -657,19 +622,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump dispatchBackScrimState(mScrimBehind.getViewAlpha()); } - private boolean shouldFadeAwayWallpaper() { - if (!mWallpaperSupportsAmbientMode) { - return false; - } - - if (mState == ScrimState.AOD - && (mDozeParameters.getAlwaysOn() || mDockManager.isDocked())) { - return true; - } - - return false; - } - public ScrimState getState() { return mState; } @@ -728,19 +680,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } - @VisibleForTesting - protected void onHideWallpaperTimeout() { - if (mState != ScrimState.AOD && mState != ScrimState.PULSING) { - return; - } - - holdWakeLock(); - mWallpaperVisibilityTimedOut = true; - mAnimateChange = true; - mAnimationDuration = mDozeParameters.getWallpaperFadeOutDuration(); - scheduleUpdate(); - } - private void holdWakeLock() { if (!mWakeLockHeld) { if (mWakeLock != null) { @@ -1171,16 +1110,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump setOrAdaptCurrentAnimation(mNotificationsScrim); setOrAdaptCurrentAnimation(mScrimInFront); dispatchBackScrimState(mScrimBehind.getViewAlpha()); - - // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING - // and docking. - if (mWallpaperVisibilityTimedOut) { - mWallpaperVisibilityTimedOut = false; - DejankUtils.postAfterTraversal(() -> { - mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(), - AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); - }); - } } /** @@ -1259,15 +1188,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump dispatchBackScrimState(mScrimBehind.getViewAlpha()); } - // We want to override the back scrim opacity for the AOD state - // when it's time to fade the wallpaper away. - boolean aodWallpaperTimeout = (mState == ScrimState.AOD || mState == ScrimState.PULSING) - && mWallpaperVisibilityTimedOut; // We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim. boolean hideFlagShowWhenLockedActivities = (mState == ScrimState.PULSING || mState == ScrimState.AOD) && mKeyguardOccluded; - if (aodWallpaperTimeout || hideFlagShowWhenLockedActivities) { + if (hideFlagShowWhenLockedActivities) { mBehindAlpha = 1; } // Prevent notification scrim flicker when transitioning away from keyguard. @@ -1668,17 +1593,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump pw.println(mPanelExpansionFraction); pw.print(" mExpansionAffectsAlpha="); pw.println(mExpansionAffectsAlpha); - - pw.print(" mState.getMaxLightRevealScrimAlpha="); - pw.println(mState.getMaxLightRevealScrimAlpha()); - } - - private void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) { - mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode; - ScrimState[] states = ScrimState.values(); - for (int i = 0; i < states.length; i++) { - states[i].setWallpaperSupportsAmbientMode(wallpaperSupportsAmbientMode); - } } /** @@ -1715,26 +1629,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump updateScrims(); } - public void setHasBackdrop(boolean hasBackdrop) { - for (ScrimState state : ScrimState.values()) { - state.setHasBackdrop(hasBackdrop); - } - - // Backdrop event may arrive after state was already applied, - // in this case, back-scrim needs to be re-evaluated - if (mState == ScrimState.AOD || mState == ScrimState.PULSING) { - float newBehindAlpha = mState.getBehindAlpha(); - if (isNaN(newBehindAlpha)) { - throw new IllegalStateException("Scrim opacity is NaN for state: " + mState - + ", back: " + mBehindAlpha); - } - if (mBehindAlpha != newBehindAlpha) { - mBehindAlpha = newBehindAlpha; - updateScrims(); - } - } - } - private void setKeyguardFadingAway(boolean fadingAway, long duration) { for (ScrimState state : ScrimState.values()) { state.setKeyguardFadingAway(fadingAway, duration); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index fbba3dc107f1..198859a9013d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -209,11 +209,6 @@ public enum ScrimState { } @Override - public float getMaxLightRevealScrimAlpha() { - return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f; - } - - @Override public boolean isLowPowerState() { return true; } @@ -237,11 +232,6 @@ public enum ScrimState { mAnimationDuration = mWakeLockScreenSensorActive ? ScrimController.ANIMATION_DURATION_LONG : ScrimController.ANIMATION_DURATION; } - @Override - public float getMaxLightRevealScrimAlpha() { - return mWakeLockScreenSensorActive ? ScrimController.WAKE_SENSOR_SCRIM_ALPHA - : AOD.getMaxLightRevealScrimAlpha(); - } }, /** @@ -368,8 +358,6 @@ public enum ScrimState { DozeParameters mDozeParameters; DockManager mDockManager; boolean mDisplayRequiresBlanking; - boolean mWallpaperSupportsAmbientMode; - boolean mHasBackdrop; boolean mLaunchingAffordanceWithPreview; boolean mOccludeAnimationPlaying; boolean mWakeLockScreenSensorActive; @@ -408,10 +396,6 @@ public enum ScrimState { return mBehindAlpha; } - public float getMaxLightRevealScrimAlpha() { - return 1f; - } - public float getNotifAlpha() { return mNotifAlpha; } @@ -473,10 +457,6 @@ public enum ScrimState { mSurfaceColor = surfaceColor; } - public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) { - mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode; - } - public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) { mLaunchingAffordanceWithPreview = launchingAffordanceWithPreview; } @@ -489,10 +469,6 @@ public enum ScrimState { return false; } - public void setHasBackdrop(boolean hasBackdrop) { - mHasBackdrop = hasBackdrop; - } - public void setWakeLockScreenSensorActive(boolean active) { mWakeLockScreenSensorActive = active; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt index 00b08f097e97..3ac0bac95d68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt @@ -18,10 +18,10 @@ package com.android.systemui.statusbar.phone import android.graphics.Rect import android.view.View -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.END_SIDE_CONTENT -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.START_SIDE_CONTENT -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.END_SIDE_CONTENT +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.START_SIDE_CONTENT +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope import com.android.systemui.util.ListenerSet import com.android.systemui.util.boundsOnScreen import javax.inject.Inject @@ -33,13 +33,13 @@ import javax.inject.Named * This is distinct from [StatusBarContentInsetsProvider], which provides the bounds of full status * bar after accounting for system insets. */ -@StatusBarFragmentScope +@HomeStatusBarScope class StatusBarBoundsProvider @Inject constructor( @Named(START_SIDE_CONTENT) private val startSideContent: View, @Named(END_SIDE_CONTENT) private val endSideContent: View, -) : StatusBarFragmentComponent.Startable { +) : HomeStatusBarComponent.Startable { interface BoundsChangeListener { fun onStatusBarBoundsChanged(bounds: BoundsPair) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt index 613efaa148f5..c6f6bd90fce6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt @@ -34,10 +34,10 @@ import com.android.systemui.Dumpable import com.android.systemui.StatusBarInsetsCommand import com.android.systemui.SysUICutoutInformation import com.android.systemui.SysUICutoutProvider -import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.res.R import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl.CacheKey import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE @@ -47,9 +47,11 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN import com.android.systemui.util.leak.RotationUtils.Rotation import com.android.systemui.util.leak.RotationUtils.getExactRotation import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import java.io.PrintWriter import java.lang.Math.max -import javax.inject.Inject /** * Encapsulates logic that can solve for the left/right insets required for the status bar contents. @@ -64,19 +66,87 @@ import javax.inject.Inject * * NOTE: This class is not threadsafe */ -@SysUISingleton -class StatusBarContentInsetsProvider -@Inject +interface StatusBarContentInsetsProvider : + CallbackController<StatusBarContentInsetsChangedListener> { + + /** + * Some views may need to care about whether or not the current top display cutout is located in + * the corner rather than somewhere in the center. In the case of a corner cutout, the status + * bar area is contiguous. + */ + fun currentRotationHasCornerCutout(): Boolean + + /** + * Calculates the maximum bounding rectangle for the privacy chip animation + ongoing privacy + * dot in the coordinates relative to the given rotation. + * + * @param rotation the rotation for which the bounds are required. This is an absolute value + * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from + * which this method is called) + */ + fun getBoundingRectForPrivacyChipForRotation( + @Rotation rotation: Int, + displayCutout: DisplayCutout?, + ): Rect + + /** + * Calculate the distance from the left, right and top edges of the screen to the status bar + * content area. This differs from the content area rects in that these values can be used + * directly as padding. + * + * @param rotation the target rotation for which to calculate insets + */ + fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets + + /** + * Calculate the insets for the status bar content in the device's current rotation + * + * @see getStatusBarContentAreaForRotation + */ + fun getStatusBarContentInsetsForCurrentRotation(): Insets + + /** + * Calculates the area of the status bar contents invariant of the current device rotation, in + * the target rotation's coordinates + * + * @param rotation the rotation for which the bounds are required. This is an absolute value + * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from + * which this method is called) + */ + fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect + + /** Get the status bar content area for the given rotation, in absolute bounds */ + fun getStatusBarContentAreaForCurrentRotation(): Rect + + fun getStatusBarPaddingTop(@Rotation rotation: Int? = null): Int + + interface Factory { + fun create( + context: Context, + configurationController: ConfigurationController, + sysUICutoutProvider: SysUICutoutProvider, + ): StatusBarContentInsetsProvider + } +} + +class StatusBarContentInsetsProviderImpl +@AssistedInject constructor( - val context: Context, - val configurationController: ConfigurationController, + @Assisted val context: Context, + @Assisted val configurationController: ConfigurationController, val dumpManager: DumpManager, val commandRegistry: CommandRegistry, - val sysUICutoutProvider: SysUICutoutProvider, -) : - CallbackController<StatusBarContentInsetsChangedListener>, - ConfigurationController.ConfigurationListener, - Dumpable { + @Assisted val sysUICutoutProvider: SysUICutoutProvider, +) : StatusBarContentInsetsProvider, ConfigurationController.ConfigurationListener, Dumpable { + + @AssistedFactory + interface Factory : StatusBarContentInsetsProvider.Factory { + override fun create( + context: Context, + configurationController: ConfigurationController, + sysUICutoutProvider: SysUICutoutProvider, + ): StatusBarContentInsetsProviderImpl + } // Limit cache size as potentially we may connect large number of displays // (e.g. network displays) @@ -95,7 +165,7 @@ constructor( object : StatusBarInsetsCommand.Callback { override fun onExecute( command: StatusBarInsetsCommand, - printWriter: PrintWriter + printWriter: PrintWriter, ) { executeCommand(command, printWriter) } @@ -133,12 +203,7 @@ constructor( listeners.forEach { it.onStatusBarContentInsetsChanged() } } - /** - * Some views may need to care about whether or not the current top display cutout is located in - * the corner rather than somewhere in the center. In the case of a corner cutout, the status - * bar area is contiguous. - */ - fun currentRotationHasCornerCutout(): Boolean { + override fun currentRotationHasCornerCutout(): Boolean { val cutout = checkNotNull(context.display).cutout ?: return false val topBounds = cutout.boundingRectTop @@ -148,17 +213,9 @@ constructor( return topBounds.left <= 0 || topBounds.right >= point.x } - /** - * Calculates the maximum bounding rectangle for the privacy chip animation + ongoing privacy - * dot in the coordinates relative to the given rotation. - * - * @param rotation the rotation for which the bounds are required. This is an absolute value - * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from - * which this method is called) - */ - fun getBoundingRectForPrivacyChipForRotation( + override fun getBoundingRectForPrivacyChipForRotation( @Rotation rotation: Int, - displayCutout: DisplayCutout? + displayCutout: DisplayCutout?, ): Rect { val key = getCacheKey(rotation, displayCutout) var insets = insetsCache[key] @@ -176,14 +233,7 @@ constructor( return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl) } - /** - * Calculate the distance from the left, right and top edges of the screen to the status bar - * content area. This differs from the content area rects in that these values can be used - * directly as padding. - * - * @param rotation the target rotation for which to calculate insets - */ - fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets = + override fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets = traceSection(tag = "StatusBarContentInsetsProvider.getStatusBarContentInsetsForRotation") { val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation() val displayCutout = sysUICutout?.cutout @@ -202,31 +252,17 @@ constructor( rotation, sysUICutout, getResourcesForRotation(rotation, context), - key + key, ) Insets.of(area.left, area.top, /* right= */ width - area.right, /* bottom= */ 0) } - /** - * Calculate the insets for the status bar content in the device's current rotation - * - * @see getStatusBarContentAreaForRotation - */ - fun getStatusBarContentInsetsForCurrentRotation(): Insets { + override fun getStatusBarContentInsetsForCurrentRotation(): Insets { return getStatusBarContentInsetsForRotation(getExactRotation(context)) } - /** - * Calculates the area of the status bar contents invariant of the current device rotation, in - * the target rotation's coordinates - * - * @param rotation the rotation for which the bounds are required. This is an absolute value - * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from - * which this method is called) - */ - @JvmOverloads - fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect { + override fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect { val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation() val displayCutout = sysUICutout?.cutout val key = getCacheKey(rotation, displayCutout) @@ -235,12 +271,11 @@ constructor( rotation, sysUICutout, getResourcesForRotation(rotation, context), - key + key, ) } - /** Get the status bar content area for the given rotation, in absolute bounds */ - fun getStatusBarContentAreaForCurrentRotation(): Rect { + override fun getStatusBarContentAreaForCurrentRotation(): Rect { val rotation = getExactRotation(context) return getStatusBarContentAreaForRotation(rotation) } @@ -249,7 +284,7 @@ constructor( @Rotation targetRotation: Int, sysUICutout: SysUICutoutInformation?, rotatedResources: Resources, - key: CacheKey + key: CacheKey, ): Rect { return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources).also { insetsCache.put(key, it) @@ -259,7 +294,7 @@ constructor( private fun getCalculatedAreaForRotation( sysUICutout: SysUICutoutInformation?, @Rotation targetRotation: Int, - rotatedResources: Resources + rotatedResources: Resources, ): Rect { val currentRotation = getExactRotation(context) @@ -299,7 +334,7 @@ constructor( configurationController.isLayoutRtl, dotWidth, bottomAlignedMargin, - statusBarContentHeight + statusBarContentHeight, ) } @@ -349,7 +384,7 @@ constructor( return resources.getDimensionPixelSize(dimenRes) } - fun getStatusBarPaddingTop(@Rotation rotation: Int? = null): Int { + override fun getStatusBarPaddingTop(@Rotation rotation: Int?): Int { val res = rotation?.let { it -> getResourcesForRotation(it, context) } ?: context.resources return res.getDimensionPixelSize(R.dimen.status_bar_padding_top) } @@ -364,13 +399,13 @@ constructor( CacheKey( rotation = rotation, displaySize = Rect(context.resources.configuration.windowConfiguration.maxBounds), - displayCutout = displayCutout + displayCutout = displayCutout, ) private data class CacheKey( @Rotation val rotation: Int, val displaySize: Rect, - val displayCutout: DisplayCutout? + val displayCutout: DisplayCutout?, ) } @@ -395,21 +430,21 @@ fun getPrivacyChipBoundingRectForInsets( contentRect: Rect, dotWidth: Int, chipWidth: Int, - isRtl: Boolean + isRtl: Boolean, ): Rect { return if (isRtl) { Rect( contentRect.left - dotWidth, contentRect.top, contentRect.left + chipWidth, - contentRect.bottom + contentRect.bottom, ) } else { Rect( contentRect.right - chipWidth, contentRect.top, contentRect.right + dotWidth, - contentRect.bottom + contentRect.bottom, ) } } @@ -443,7 +478,7 @@ fun calculateInsetsForRotationWithRotatedResources( isRtl: Boolean, dotWidth: Int, bottomAlignedMargin: Int, - statusBarContentHeight: Int + statusBarContentHeight: Int, ): Rect { /* TODO: Check if this is ever used for devices with no rounded corners @@ -467,7 +502,7 @@ fun calculateInsetsForRotationWithRotatedResources( targetRotation, currentRotation, bottomAlignedMargin, - statusBarContentHeight + statusBarContentHeight, ) } @@ -503,7 +538,7 @@ private fun getStatusBarContentBounds( @Rotation targetRotation: Int, @Rotation currentRotation: Int, bottomAlignedMargin: Int, - statusBarContentHeight: Int + statusBarContentHeight: Int, ): Rect { val insetTop = getInsetTop(bottomAlignedMargin, statusBarContentHeight, sbHeight) @@ -597,7 +632,7 @@ private val DisplayCutout.boundingRectsLeftRightTop private fun getInsetTop( bottomAlignedMargin: Int, statusBarContentHeight: Int, - statusBarHeight: Int + statusBarHeight: Int, ): Int { val bottomAlignmentEnabled = bottomAlignedMargin >= 0 if (!bottomAlignmentEnabled) { @@ -610,7 +645,7 @@ private fun getInsetTop( private fun sbRect( @Rotation relativeRotation: Int, sbHeight: Int, - displaySize: Pair<Int, Int> + displaySize: Pair<Int, Int>, ): Rect { val w = displaySize.first val h = displaySize.second @@ -626,7 +661,7 @@ private fun shareShortEdge( sbRect: Rect, cutoutRect: Rect, currentWidth: Int, - currentHeight: Int + currentHeight: Int, ): Boolean { if (currentWidth < currentHeight) { // Check top/bottom edges by extending the width of the display cutout rect and checking diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java index 25b8bfe0da25..1afe416c43c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java @@ -21,7 +21,7 @@ import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_SE import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT; import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_WARNING; -import static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.OPERATOR_NAME_VIEW; +import static com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.OPERATOR_NAME_VIEW; import android.annotation.NonNull; import android.os.Bundle; @@ -32,7 +32,7 @@ import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeCommandReceiver; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.navigationbar.NavigationBarController; -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope; +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.util.ViewController; @@ -48,7 +48,7 @@ import javax.inject.Named; * This class extends ViewController not because it controls a specific view, but because we want it * to get torn down and re-created in line with the view's lifecycle. */ -@StatusBarFragmentScope +@HomeStatusBarScope public class StatusBarDemoMode extends ViewController<View> implements DemoMode { private final Clock mClockView; private final View mOperatorNameView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 17bd53869ee5..74c6e72d3400 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -47,7 +47,6 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; @@ -68,7 +67,6 @@ import com.android.systemui.bouncer.util.BouncerTestUtilsKt; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.keyguard.DismissCallbackRegistry; @@ -77,9 +75,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInte import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.DismissAction; -import com.android.systemui.keyguard.shared.model.Edge; import com.android.systemui.keyguard.shared.model.KeyguardDone; -import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.TaskbarDelegate; @@ -165,7 +161,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final DreamOverlayStateController mDreamOverlayStateController; @Nullable private final FoldAodAnimationController mFoldAodAnimationController; - KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController; private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor; private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; private final AlternateBouncerInteractor mAlternateBouncerInteractor; @@ -463,11 +458,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb onPanelExpansionChanged(currentState); } mNotificationContainer = notificationContainer; - if (!DeviceEntryUdfpsRefactor.isEnabled()) { - mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create( - centralSurfaces.getKeyguardMessageArea()); - } - mCentralSurfacesRegistered = true; registerListeners(); @@ -518,24 +508,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mListenForCanShowAlternateBouncer.cancel(null); } mListenForCanShowAlternateBouncer = null; - if (!DeviceEntryUdfpsRefactor.isEnabled()) { - mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow( - mKeyguardTransitionInteractor - .transition(Edge.create(KeyguardState.ALTERNATE_BOUNCER)), - this::consumeFromAlternateBouncerTransitionSteps - ); - - mListenForKeyguardAuthenticatedBiometricsHandled = mJavaAdapter.alwaysCollectFlow( - mPrimaryBouncerInteractor.getKeyguardAuthenticatedBiometricsHandled(), - this::consumeKeyguardAuthenticatedBiometricsHandled - ); - } else { - // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot. - mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow( - mAlternateBouncerInteractor.getCanShowAlternateBouncer(), - this::consumeCanShowAlternateBouncer - ); - } + // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot. + mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow( + mAlternateBouncerInteractor.getCanShowAlternateBouncer(), + this::consumeCanShowAlternateBouncer + ); if (KeyguardWmStateRefactor.isEnabled()) { // Show the keyguard views whenever we've told WM that the lockscreen is visible. @@ -792,21 +769,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; } - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) { - Log.d(TAG, "showBouncer:alternateBouncer.forceShow()"); - mAlternateBouncerInteractor.forceShow(); - updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); - } else { - showPrimaryBouncer(scrimmed); - } - return; - } - - if (!mAlternateBouncerInteractor.show()) { - showPrimaryBouncer(scrimmed); - } else { + if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) { + Log.d(TAG, "showBouncer:alternateBouncer.forceShow()"); + mAlternateBouncerInteractor.forceShow(); updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); + } else { + showPrimaryBouncer(scrimmed); } } @@ -921,13 +889,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardGoneCancelAction = null; } - if (DeviceEntryUdfpsRefactor.isEnabled()) { - Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()"); - mAlternateBouncerInteractor.forceShow(); - updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); - } else { - updateAlternateBouncerShowing(mAlternateBouncerInteractor.show()); - } + Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()"); + mAlternateBouncerInteractor.forceShow(); + updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); setKeyguardMessage(message, null, null); return; } @@ -1033,11 +997,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } final boolean isShowingAlternateBouncer = mAlternateBouncerInteractor.isVisibleState(); - if (mKeyguardMessageAreaController != null) { - DeviceEntryUdfpsRefactor.assertInLegacyMode(); - mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer); - mKeyguardMessageAreaController.setMessage(""); - } if (!SceneContainerFlag.isEnabled()) { mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer); } @@ -1646,12 +1605,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb /** Display security message to relevant KeyguardMessageArea. */ public void setKeyguardMessage(String message, ColorStateList colorState, BiometricSourceType biometricSourceType) { - if (mAlternateBouncerInteractor.isVisibleState()) { - if (mKeyguardMessageAreaController != null) { - DeviceEntryUdfpsRefactor.assertInLegacyMode(); - mKeyguardMessageAreaController.setMessage(message, biometricSourceType); - } - } else { + if (!mAlternateBouncerInteractor.isVisibleState()) { mPrimaryBouncerInteractor.showMessage(message, colorState); } } @@ -1778,66 +1732,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } - /** - * An opportunity for the AlternateBouncer to handle the touch instead of sending - * the touch to NPVC child views. - * @return true if the alternate bouncer should consime the touch and prevent it from - * going to its child views - */ - public boolean dispatchTouchEvent(MotionEvent event) { - if (shouldInterceptTouchEvent(event) - && !mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(event)) { - onTouch(event); - } - return shouldInterceptTouchEvent(event); - } - - /** - * Whether the touch should be intercepted by the AlternateBouncer before going to the - * notification shade's child views. - */ - public boolean shouldInterceptTouchEvent(MotionEvent event) { - if (DeviceEntryUdfpsRefactor.isEnabled()) { - return false; - } - return mAlternateBouncerInteractor.isVisibleState(); - } - - /** - * For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently - * showing. - */ - public boolean onTouch(MotionEvent event) { - if (DeviceEntryUdfpsRefactor.isEnabled()) { - return false; - } - - boolean handleTouch = shouldInterceptTouchEvent(event); - if (handleTouch) { - final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN; - final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch() - && event.getActionMasked() == MotionEvent.ACTION_UP; - final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade = - mKeyguardUpdateManager.isUdfpsEnrolled(); - final boolean actionOutsideShouldDismissAlternateBouncer = - event.getActionMasked() == MotionEvent.ACTION_OUTSIDE - && !udfpsOverlayWillForwardEventsOutsideNotificationShade; - if (actionDown) { - mAlternateBouncerInteractor.setReceivedDownTouch(true); - } else if ((actionDownThenUp || actionOutsideShouldDismissAlternateBouncer) - && mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) { - showPrimaryBouncer(true); - } - } - - // Forward NPVC touches to callbacks in case they want to respond to touches - for (KeyguardViewManagerCallback callback: mCallbacks) { - callback.onTouch(event); - } - - return handleTouch; - } - /** Update keyguard position based on a tapped X coordinate. */ public void updateKeyguardPosition(float x) { mPrimaryBouncerInteractor.setKeyguardPosition(x); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt index 5f864e5dc53a..09e191dd1911 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt @@ -18,10 +18,12 @@ package com.android.systemui.statusbar.phone.dagger import android.view.Display import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Default import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.core.CommandQueueInitializer import com.android.systemui.statusbar.core.MultiDisplayStatusBarInitializerStore +import com.android.systemui.statusbar.core.MultiDisplayStatusBarStarter import com.android.systemui.statusbar.core.SingleDisplayStatusBarInitializerStore import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.core.StatusBarInitializer @@ -29,7 +31,9 @@ import com.android.systemui.statusbar.core.StatusBarInitializerImpl import com.android.systemui.statusbar.core.StatusBarInitializerStore import com.android.systemui.statusbar.core.StatusBarOrchestrator import com.android.systemui.statusbar.core.StatusBarSimpleFragment +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStoreImpl import dagger.Binds @@ -38,6 +42,7 @@ import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap +import kotlinx.coroutines.CoroutineScope /** Similar in purpose to [StatusBarModule], but scoped only to phones */ @Module @@ -58,24 +63,56 @@ interface StatusBarPhoneModule { implFactory: StatusBarInitializerImpl.Factory ): StatusBarInitializer.Factory - /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */ - @Binds - @IntoMap - @ClassKey(StatusBarInitializerImpl::class) - fun bindStatusBarInitializer(@Default impl: StatusBarInitializerImpl): CoreStartable - @Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer companion object { + /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */ + @Provides + @SysUISingleton + @IntoMap + @ClassKey(StatusBarInitializer::class) + fun bindStatusBarInitializer( + @Default defaultInitializerLazy: Lazy<StatusBarInitializerImpl> + ): CoreStartable { + return if (StatusBarConnectedDisplays.isEnabled) { + // Will be started through MultiDisplayStatusBarStarter + CoreStartable.NOP + } else { + defaultInitializerLazy.get() + } + } + // Dagger doesn't support providing AssistedInject types, without a qualifier. Using the // Default qualifier for this reason. @Default @Provides @SysUISingleton fun statusBarInitializerImpl( - implFactory: StatusBarInitializerImpl.Factory + implFactory: StatusBarInitializerImpl.Factory, + statusBarWindowControllerStore: StatusBarWindowControllerStore, ): StatusBarInitializerImpl { - return implFactory.create(displayId = Display.DEFAULT_DISPLAY) + return implFactory.create(statusBarWindowControllerStore.defaultDisplay) + } + + @Provides + @SysUISingleton + @Default // Dagger does not support providing @AssistedInject types without a qualifier + fun orchestrator( + @Background backgroundApplicationScope: CoroutineScope, + statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore, + statusBarModeRepositoryStore: StatusBarModeRepositoryStore, + initializerStore: StatusBarInitializerStore, + statusBarWindowControllerStore: StatusBarWindowControllerStore, + statusBarOrchestratorFactory: StatusBarOrchestrator.Factory, + ): StatusBarOrchestrator { + return statusBarOrchestratorFactory.create( + Display.DEFAULT_DISPLAY, + backgroundApplicationScope, + statusBarWindowStateRepositoryStore.defaultDisplay, + statusBarModeRepositoryStore.defaultDisplay, + initializerStore.defaultDisplay, + statusBarWindowControllerStore.defaultDisplay, + ) } @Provides @@ -83,11 +120,29 @@ interface StatusBarPhoneModule { @IntoMap @ClassKey(StatusBarOrchestrator::class) fun orchestratorCoreStartable( - orchestratorLazy: Lazy<StatusBarOrchestrator> + @Default orchestratorLazy: Lazy<StatusBarOrchestrator> ): CoreStartable { - return if (StatusBarSimpleFragment.isEnabled) { + return if (StatusBarConnectedDisplays.isEnabled) { + // Will be started through MultiDisplayStatusBarStarter + CoreStartable.NOP + } else if (StatusBarSimpleFragment.isEnabled) { orchestratorLazy.get() } else { + // Will be started through CentralSurfacesImpl + CoreStartable.NOP + } + } + + @Provides + @SysUISingleton + @IntoMap + @ClassKey(MultiDisplayStatusBarStarter::class) + fun multiDisplayStarter( + multiDisplayStatusBarStarterLazy: Lazy<MultiDisplayStatusBarStarter> + ): CoreStartable { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayStatusBarStarterLazy.get() + } else { CoreStartable.NOP } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index c258095510c6..37c8c637c804 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -29,6 +29,7 @@ import android.telephony.SubscriptionManager; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.SparseArray; +import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -55,7 +56,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameView; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips; +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.core.StatusBarSimpleFragment; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; @@ -65,15 +66,15 @@ import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager; import com.android.systemui.statusbar.phone.StatusBarLocation; -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent; -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable; +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent; +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent.Startable; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.phone.ui.DarkIconManager; import com.android.systemui.statusbar.phone.ui.StatusBarIconController; -import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder; +import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder; import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener; -import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel; +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; @@ -114,7 +115,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public static final int FADE_IN_DELAY = 50; private static final int SOURCE_SYSTEM_EVENT_ANIMATOR = 1; private static final int SOURCE_OTHER = 2; - private StatusBarFragmentComponent mStatusBarFragmentComponent; + private HomeStatusBarComponent mHomeStatusBarComponent; private PhoneStatusBarView mStatusBar; private final StatusBarStateController mStatusBarStateController; private final KeyguardStateController mKeyguardStateController; @@ -133,7 +134,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private StatusBarVisibilityModel mLastModifiedVisibility = StatusBarVisibilityModel.createDefaultModel(); private DarkIconManager mDarkIconManager; - private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory; + private final HomeStatusBarComponent.Factory mHomeStatusBarComponentFactory; private final CommandQueue mCommandQueue; private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger; private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory; @@ -142,8 +143,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final ShadeExpansionStateManager mShadeExpansionStateManager; private final StatusBarIconController mStatusBarIconController; private final CarrierConfigTracker mCarrierConfigTracker; - private final CollapsedStatusBarViewModel mCollapsedStatusBarViewModel; - private final CollapsedStatusBarViewBinder mCollapsedStatusBarViewBinder; + private final HomeStatusBarViewModel mHomeStatusBarViewModel; + private final HomeStatusBarViewBinder mHomeStatusBarViewBinder; private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; private final DarkIconManager.Factory mDarkIconManagerFactory; private final SecureSettings mSecureSettings; @@ -238,14 +239,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Inject public CollapsedStatusBarFragment( - StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory, + HomeStatusBarComponent.Factory homeStatusBarComponentFactory, OngoingCallController ongoingCallController, SystemStatusAnimationScheduler animationScheduler, ShadeExpansionStateManager shadeExpansionStateManager, StatusBarIconController statusBarIconController, DarkIconManager.Factory darkIconManagerFactory, - CollapsedStatusBarViewModel collapsedStatusBarViewModel, - CollapsedStatusBarViewBinder collapsedStatusBarViewBinder, + HomeStatusBarViewModel homeStatusBarViewModel, + HomeStatusBarViewBinder homeStatusBarViewBinder, StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager, KeyguardStateController keyguardStateController, PanelExpansionInteractor panelExpansionInteractor, @@ -261,13 +262,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue StatusBarWindowStateController statusBarWindowStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, DemoModeController demoModeController) { - mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory; + mHomeStatusBarComponentFactory = homeStatusBarComponentFactory; mOngoingCallController = ongoingCallController; mAnimationScheduler = animationScheduler; mShadeExpansionStateManager = shadeExpansionStateManager; mStatusBarIconController = statusBarIconController; - mCollapsedStatusBarViewModel = collapsedStatusBarViewModel; - mCollapsedStatusBarViewBinder = collapsedStatusBarViewBinder; + mHomeStatusBarViewModel = homeStatusBarViewModel; + mHomeStatusBarViewBinder = homeStatusBarViewBinder; mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager; mDarkIconManagerFactory = darkIconManagerFactory; mKeyguardStateController = keyguardStateController; @@ -333,12 +334,12 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - mDumpManager.registerDumpable(getClass().getSimpleName(), this); - mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create( + mDumpManager.registerDumpable(getDumpableName(), this); + mHomeStatusBarComponent = mHomeStatusBarComponentFactory.create( (PhoneStatusBarView) getView()); - mStatusBarFragmentComponent.init(); + mHomeStatusBarComponent.init(); mStartableStates.clear(); - for (Startable startable : mStatusBarFragmentComponent.getStartables()) { + for (Startable startable : mHomeStatusBarComponent.getStartables()) { mStartableStates.put(startable, Startable.State.STARTING); startable.start(); mStartableStates.put(startable, Startable.State.STARTED); @@ -370,8 +371,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mCarrierConfigTracker.addCallback(mCarrierConfigCallback); mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); - mCollapsedStatusBarViewBinder.bind( - mStatusBar, mCollapsedStatusBarViewModel, mStatusBarVisibilityChangeListener); + mHomeStatusBarViewBinder.bind( + mStatusBar, mHomeStatusBarViewModel, mStatusBarVisibilityChangeListener); + } + + private String getDumpableName() { + if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) { + return getClass().getSimpleName(); + } else { + return getClass().getSimpleName() + getContext().getDisplayId(); + } } @Override @@ -465,12 +474,12 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mCarrierConfigTracker.removeCallback(mCarrierConfigCallback); mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener); - for (Startable startable : mStatusBarFragmentComponent.getStartables()) { + for (Startable startable : mHomeStatusBarComponent.getStartables()) { mStartableStates.put(startable, Startable.State.STOPPING); startable.stop(); mStartableStates.put(startable, Startable.State.STOPPED); } - mDumpManager.unregisterDumpable(getClass().getSimpleName()); + mDumpManager.unregisterDumpable(getDumpableName()); if (mNicBindingDisposable != null) { mNicBindingDisposable.dispose(); mNicBindingDisposable = null; @@ -486,7 +495,12 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue NotificationIconContainer notificationIcons = notificationIconArea.requireViewById(R.id.notificationIcons); mNotificationIconAreaInner = notificationIcons; - mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons); + if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) { + //TODO(b/369337701): implement notification icons for all displays. + // Currently if we try to bind for all displays, there is a crash, because the same + // notification icon view can't have multiple parents. + mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons); + } if (!StatusBarSimpleFragment.isEnabled()) { updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false); @@ -501,8 +515,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue * fragment functionality and we won't need to expose it here anymore. */ @Nullable - public StatusBarFragmentComponent getStatusBarFragmentComponent() { - return mStatusBarFragmentComponent; + public HomeStatusBarComponent getHomeStatusBarComponent() { + return mHomeStatusBarComponent; } private StatusBarVisibilityChangeListener mStatusBarVisibilityChangeListener = @@ -608,7 +622,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue // TODO(b/328393714) use HeadsUpNotificationInteractor.showHeadsUpStatusBar instead. boolean headsUpVisible = - mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible(); + mHomeStatusBarComponent.getHeadsUpAppearanceController().shouldBeVisible(); if (SceneContainerFlag.isEnabled()) { // With the scene container, only use the value calculated by the view model to @@ -642,7 +656,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } boolean showSecondaryOngoingActivityChip = Flags.statusBarScreenSharingChips() - && StatusBarRonChips.isEnabled() + && StatusBarNotifChips.isEnabled() && mHasSecondaryOngoingActivity; return new StatusBarVisibilityModel( @@ -684,7 +698,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue boolean showSecondaryOngoingActivityChip = // Secondary chips are only supported when RONs are enabled. - StatusBarRonChips.isEnabled() + StatusBarNotifChips.isEnabled() && visibilityModel.getShowSecondaryOngoingActivityChip() && !disableNotifications; if (showSecondaryOngoingActivityChip) { @@ -743,7 +757,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue // transition to occluding to finish before allowing us to potentially show the status bar // again. (This status bar is always hidden on keyguard, so it's safe to continue hiding it // during this transition.) See b/273314977. - if (mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().getValue()) { + if (mHomeStatusBarViewModel.isTransitioningFromLockscreenToOccluded().getValue()) { return true; } @@ -811,7 +825,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void showSecondaryOngoingActivityChip(boolean animate) { - StatusBarRonChips.assertInNewMode(); + StatusBarNotifChips.assertInNewMode(); StatusBarSimpleFragment.assertInLegacyMode(); animateShow(mSecondaryOngoingActivityChip, animate); } @@ -983,7 +997,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue pw.println("mHasPrimaryOngoingActivity=" + mHasPrimaryOngoingActivity); pw.println("mHasSecondaryOngoingActivity=" + mHasSecondaryOngoingActivity); pw.println("mAnimationsEnabled=" + mAnimationsEnabled); - StatusBarFragmentComponent component = mStatusBarFragmentComponent; + HomeStatusBarComponent component = mHomeStatusBarComponent; if (component == null) { pw.println("StatusBarFragmentComponent is null"); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentStartable.kt index 55af0e3d65a3..94006f6cf1a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentStartable.kt @@ -20,7 +20,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.fragments.FragmentService import com.android.systemui.qs.QSFragmentStartable -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent import dagger.Binds import dagger.Module import dagger.multibindings.ClassKey @@ -37,7 +37,7 @@ class CollapsedStatusBarFragmentStartable @Inject constructor( private val fragmentService: FragmentService, - private val collapsedstatusBarFragmentProvider: Provider<CollapsedStatusBarFragment> + private val collapsedstatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>, ) : CoreStartable { override fun start() { fragmentService.addFragmentInstantiationProvider( @@ -47,7 +47,7 @@ constructor( } } -@Module(subcomponents = [StatusBarFragmentComponent::class]) +@Module(subcomponents = [HomeStatusBarComponent::class]) interface CollapsedStatusBarFragmentStartableModule { @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java index 96faa359d43e..d4cb625d3722 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java @@ -42,27 +42,29 @@ import java.util.Set; * fragment is recreated. * * Anything that depends on {@link CollapsedStatusBarFragment} or {@link PhoneStatusBarView} - * should be included here or in {@link StatusBarFragmentModule}. + * should be included here or in {@link HomeStatusBarModule}. */ - @Subcomponent(modules = { - StatusBarFragmentModule.class, + HomeStatusBarModule.class, StatusBarStartablesModule.class }) -@StatusBarFragmentScope -public interface StatusBarFragmentComponent { +@HomeStatusBarScope +public interface HomeStatusBarComponent { /** Simple factory. */ @Subcomponent.Factory interface Factory { - StatusBarFragmentComponent create( + /** */ + HomeStatusBarComponent create( @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView); } /** - * Performs initialization logic after {@link StatusBarFragmentComponent} has been constructed. + * Performs initialization logic after {@link HomeStatusBarComponent} has been constructed. */ interface Startable { + /** */ void start(); + /** */ void stop(); enum State { @@ -86,32 +88,32 @@ public interface StatusBarFragmentComponent { } /** */ - @StatusBarFragmentScope + @HomeStatusBarScope BatteryMeterViewController getBatteryMeterViewController(); /** */ - @StatusBarFragmentScope + @HomeStatusBarScope @RootView PhoneStatusBarView getPhoneStatusBarView(); /** */ - @StatusBarFragmentScope + @HomeStatusBarScope PhoneStatusBarViewController getPhoneStatusBarViewController(); /** */ - @StatusBarFragmentScope + @HomeStatusBarScope HeadsUpAppearanceController getHeadsUpAppearanceController(); /** */ - @StatusBarFragmentScope + @HomeStatusBarScope LegacyLightsOutNotifController getLegacyLightsOutNotifController(); /** */ - @StatusBarFragmentScope + @HomeStatusBarScope StatusBarDemoMode getStatusBarDemoMode(); /** */ - @StatusBarFragmentScope + @HomeStatusBarScope PhoneStatusBarTransitions getPhoneStatusBarTransitions(); /** */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java index cf877a741d6b..f6f8adb851e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java @@ -20,15 +20,17 @@ import android.view.View; import android.view.ViewStub; import com.android.systemui.battery.BatteryMeterView; +import com.android.systemui.dagger.qualifiers.DisplaySpecific; import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.res.R; import com.android.systemui.statusbar.HeadsUpStatusBarView; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.Clock; -import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import dagger.Module; @@ -38,9 +40,9 @@ import java.util.Optional; import javax.inject.Named; -/** Dagger module for {@link StatusBarFragmentComponent}. */ +/** Dagger module for {@link HomeStatusBarComponent}. */ @Module -public interface StatusBarFragmentModule { +public interface HomeStatusBarModule { String LIGHTS_OUT_NOTIF_VIEW = "lights_out_notif_view"; String OPERATOR_NAME_VIEW = "operator_name_view"; @@ -50,21 +52,21 @@ public interface StatusBarFragmentModule { /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.battery); } /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope static StatusBarLocation getStatusBarLocation() { return StatusBarLocation.HOME; } /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope @Named(START_SIDE_CONTENT) static View startSideContent(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.status_bar_start_side_content); @@ -72,7 +74,7 @@ public interface StatusBarFragmentModule { /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope @Named(END_SIDE_CONTENT) static View endSideContent(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.status_bar_end_side_content); @@ -80,7 +82,7 @@ public interface StatusBarFragmentModule { /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope @Named(LIGHTS_OUT_NOTIF_VIEW) static View provideLightsOutNotifView(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.notification_lights_out); @@ -88,7 +90,7 @@ public interface StatusBarFragmentModule { /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope @Named(OPERATOR_NAME_VIEW) static View provideOperatorNameView(@RootView PhoneStatusBarView view) { View operatorName = ((ViewStub) view.findViewById(R.id.operator_name_stub)).inflate(); @@ -98,7 +100,7 @@ public interface StatusBarFragmentModule { /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope @Named(OPERATOR_NAME_FRAME_VIEW) static Optional<View> provideOperatorFrameNameView(@RootView PhoneStatusBarView view) { return Optional.ofNullable(view.findViewById(R.id.operator_name_frame)); @@ -106,14 +108,14 @@ public interface StatusBarFragmentModule { /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope static Clock provideClock(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.clock); } /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope static PhoneStatusBarViewController providePhoneStatusBarViewController( PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory, @RootView PhoneStatusBarView phoneStatusBarView) { @@ -123,7 +125,7 @@ public interface StatusBarFragmentModule { /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope static PhoneStatusBarTransitions providePhoneStatusBarTransitions( @RootView PhoneStatusBarView view, StatusBarWindowControllerStore statusBarWindowControllerStore) { @@ -133,8 +135,25 @@ public interface StatusBarFragmentModule { /** */ @Provides - @StatusBarFragmentScope + @HomeStatusBarScope static HeadsUpStatusBarView providesHeasdUpStatusBarView(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.heads_up_status_bar_view); } + + /** */ + @Provides + @HomeStatusBarScope + @DisplaySpecific + static int displayId(@RootView PhoneStatusBarView view) { + return view.getContext().getDisplayId(); + } + + /** */ + @Provides + @HomeStatusBarScope + static StatusBarConfigurationController configurationController( + @DisplaySpecific int displayId, StatusBarConfigurationControllerStore store) { + return store.forDisplay(displayId); + } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarScope.java index 96cff5960d68..2b1edddacd8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarScope.java @@ -24,9 +24,9 @@ import java.lang.annotation.Retention; import javax.inject.Scope; /** - * Scope annotation for singleton items within the {@link StatusBarFragmentComponent}. + * Scope annotation for singleton items within the {@link HomeStatusBarComponent}. */ @Documented @Retention(RUNTIME) @Scope -public @interface StatusBarFragmentScope {} +public @interface HomeStatusBarScope {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt index 9003d13df0a0..ba9181436fb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt @@ -28,5 +28,5 @@ internal interface StatusBarStartablesModule { @IntoSet fun statusBarBoundsCalculator( statusBarBoundsProvider: StatusBarBoundsProvider - ): StatusBarFragmentComponent.Startable + ): HomeStatusBarComponent.Startable } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt index 48500499f537..935b1012be31 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt @@ -48,10 +48,10 @@ import com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel.DeviceBase import com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel.DeviceBasedSatelliteViewModelImpl import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl -import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder -import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinderImpl -import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel -import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModelImpl +import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder +import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinderImpl +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModelImpl import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher @@ -131,14 +131,10 @@ abstract class StatusBarPipelineModule { abstract fun bindCarrierConfigStartable(impl: CarrierConfigCoreStartable): CoreStartable @Binds - abstract fun collapsedStatusBarViewModel( - impl: CollapsedStatusBarViewModelImpl - ): CollapsedStatusBarViewModel + abstract fun homeStatusBarViewModel(impl: HomeStatusBarViewModelImpl): HomeStatusBarViewModel @Binds - abstract fun collapsedStatusBarViewBinder( - impl: CollapsedStatusBarViewBinderImpl - ): CollapsedStatusBarViewBinder + abstract fun homeStatusBarViewBinder(impl: HomeStatusBarViewBinderImpl): HomeStatusBarViewBinder companion object { @@ -162,7 +158,7 @@ abstract class StatusBarPipelineModule { @SysUISingleton @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON) fun provideFirstMobileSubShowingNetworkTypeIconProvider( - mobileIconsViewModel: MobileIconsViewModel, + mobileIconsViewModel: MobileIconsViewModel ): Supplier<Flow<Boolean>> { return Supplier<Flow<Boolean>> { mobileIconsViewModel.firstMobileSubShowingNetworkTypeIcon diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt index bad6f80c3735..694a5e529ec4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel -import com.android.app.tracing.coroutines.createCoroutineTracingContext import androidx.annotation.VisibleForTesting +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlagsClassic @@ -29,8 +29,8 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.cancel @@ -116,7 +116,7 @@ constructor( private fun createViewModel(subId: Int): Pair<MobileIconViewModel, CoroutineScope> { // Create a child scope so we can cancel it - val vmScope = scope.createChildScope(createCoroutineTracingContext("MobileIconViewModel")) + val vmScope = scope.createChildScope(newTracingContext("MobileIconViewModel")) val vm = MobileIconViewModel( subId, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt index 9c168be0693f..8d7b57db4125 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt @@ -27,36 +27,38 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.core.StatusBarSimpleFragment import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment -import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel import javax.inject.Inject import kotlinx.coroutines.launch /** - * Interface to assist with binding the [CollapsedStatusBarFragment] to - * [CollapsedStatusBarViewModel]. Used only to enable easy testing of [CollapsedStatusBarFragment]. + * Interface to assist with binding the [CollapsedStatusBarFragment] to [HomeStatusBarViewModel]. + * Used only to enable easy testing of [CollapsedStatusBarFragment]. */ -interface CollapsedStatusBarViewBinder { +interface HomeStatusBarViewBinder { /** * Binds the view to the view-model. [listener] will be notified whenever an event that may * change the status bar visibility occurs. */ fun bind( view: View, - viewModel: CollapsedStatusBarViewModel, + viewModel: HomeStatusBarViewModel, listener: StatusBarVisibilityChangeListener, ) } @SysUISingleton -class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBarViewBinder { +class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinder { override fun bind( view: View, - viewModel: CollapsedStatusBarViewModel, + viewModel: HomeStatusBarViewModel, listener: StatusBarVisibilityChangeListener, ) { view.repeatWhenAttached { @@ -83,7 +85,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } - if (Flags.statusBarScreenSharingChips() && !Flags.statusBarRonChips()) { + if (Flags.statusBarScreenSharingChips() && !StatusBarNotifChips.isEnabled) { val primaryChipView: View = view.requireViewById(R.id.ongoing_activity_chip_primary) launch { @@ -119,7 +121,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } - if (Flags.statusBarScreenSharingChips() && Flags.statusBarRonChips()) { + if (Flags.statusBarScreenSharingChips() && StatusBarNotifChips.isEnabled) { val primaryChipView: View = view.requireViewById(R.id.ongoing_activity_chip_primary) val secondaryChipView: View = @@ -184,9 +186,8 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } - private fun OngoingActivityChipModel.toVisibilityModel(): - CollapsedStatusBarViewModel.VisibilityModel { - return CollapsedStatusBarViewModel.VisibilityModel( + private fun OngoingActivityChipModel.toVisibilityModel(): VisibilityModel { + return VisibilityModel( visibility = if (this is OngoingActivityChipModel.Shown) View.VISIBLE else View.GONE, // TODO(b/364653005): Figure out the animation story here. shouldAnimateChange = true, @@ -223,7 +224,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa .start() } - private fun View.adjustVisibility(model: CollapsedStatusBarViewModel.VisibilityModel) { + private fun View.adjustVisibility(model: VisibilityModel) { if (model.visibility == View.VISIBLE) { this.show(model.shouldAnimateChange) } else { @@ -297,7 +298,7 @@ interface StatusBarVisibilityChangeListener { /** * Called when the scene state has changed such that the home status bar is newly allowed or no - * longer allowed. See [CollapsedStatusBarViewModel.isHomeStatusBarAllowedByScene]. + * longer allowed. See [HomeStatusBarViewModel.isHomeStatusBarAllowedByScene]. */ fun onIsHomeStatusBarAllowedBySceneChanged(isHomeStatusBarAllowedByScene: Boolean) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/RetroText.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/RetroText.kt new file mode 100644 index 000000000000..71bbdeaeb475 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/RetroText.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.shared.ui.composable + +import androidx.compose.foundation.layout.offset +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +private val retroColors = + listOf( + Color(0xFFEADFB4), // beige + Color(0xFF9BB0C1), // gray-blue + Color(0xFFF6995C), // orange + Color(0xFF51829B), // cyan + ) + +/** Render a single string multiple times (with offsets) kinda like retro vintage text */ +@Composable +fun RetroText(text: String = "") { + // Render the text for each retroColor, and then once for the foreground + for (i in retroColors.size downTo 1) { + val color = retroColors[i - 1] + RetroTextLayer(text = text, color = color, (-1.5 * i).dp, i.dp) + } + + RetroTextLayer(text = text, color = Color.Black, ox = 0.dp, oy = 0.dp) +} + +@Composable +fun RetroTextLayer(text: String, color: Color, ox: Dp, oy: Dp) { + Text( + text = text, + modifier = Modifier.offset(ox, oy), + textAlign = TextAlign.Center, + style = + TextStyle( + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + fontStyle = FontStyle.Italic, + color = color, + ), + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt new file mode 100644 index 000000000000..a21cc22ad03f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.shared.ui.composable + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.android.systemui.res.R +import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder +import com.android.systemui.statusbar.phone.NotificationIconContainer +import com.android.systemui.statusbar.phone.PhoneStatusBarView +import com.android.systemui.statusbar.phone.StatusBarLocation +import com.android.systemui.statusbar.phone.StatusIconContainer +import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController +import com.android.systemui.statusbar.phone.ui.DarkIconManager +import com.android.systemui.statusbar.phone.ui.StatusBarIconController +import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder +import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel +import javax.inject.Inject +import kotlinx.coroutines.launch + +/** Factory to simplify the dependency management for [StatusBarRoot] */ +class StatusBarRootFactory +@Inject +constructor( + private val homeStatusBarViewModel: HomeStatusBarViewModel, + private val homeStatusBarViewBinder: HomeStatusBarViewBinder, + private val notificationIconsBinder: NotificationIconContainerStatusBarViewBinder, + private val darkIconManagerFactory: DarkIconManager.Factory, + private val iconController: StatusBarIconController, + private val ongoingCallController: OngoingCallController, +) { + fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView { + val composeView = ComposeView(root.context) + composeView.apply { + setContent { + StatusBarRoot( + parent = root, + statusBarViewModel = homeStatusBarViewModel, + statusBarViewBinder = homeStatusBarViewBinder, + notificationIconsBinder = notificationIconsBinder, + darkIconManagerFactory = darkIconManagerFactory, + iconController = iconController, + ongoingCallController = ongoingCallController, + onViewCreated = andThen, + ) + } + } + + return composeView + } +} + +/** + * For now, this class exists only to replace the former CollapsedStatusBarFragment. We simply stand + * up the PhoneStatusBarView here (allowing the component to be initialized from the [init] block). + * This is the place, for now, where we can manually set up lingering dependencies that came from + * the fragment until we can move them to recommended-arch style repos. + * + * @param onViewCreated called immediately after the view is inflated, and takes as a parameter the + * newly-inflated PhoneStatusBarView. This lambda is useful for tying together old initialization + * logic until it can be replaced. + */ +@Composable +fun StatusBarRoot( + parent: ViewGroup, + statusBarViewModel: HomeStatusBarViewModel, + statusBarViewBinder: HomeStatusBarViewBinder, + notificationIconsBinder: NotificationIconContainerStatusBarViewBinder, + darkIconManagerFactory: DarkIconManager.Factory, + iconController: StatusBarIconController, + ongoingCallController: OngoingCallController, + onViewCreated: (ViewGroup) -> Unit, +) { + // None of these methods are used when [StatusBarSimpleFragment] is on. + // This can be deleted once the fragment is gone + val nopVisibilityChangeListener = + object : StatusBarVisibilityChangeListener { + override fun onStatusBarVisibilityMaybeChanged() {} + + override fun onTransitionFromLockscreenToDreamStarted() {} + + override fun onOngoingActivityStatusChanged( + hasPrimaryOngoingActivity: Boolean, + hasSecondaryOngoingActivity: Boolean, + shouldAnimate: Boolean, + ) {} + + override fun onIsHomeStatusBarAllowedBySceneChanged( + isHomeStatusBarAllowedByScene: Boolean + ) {} + } + + Box(Modifier.fillMaxSize()) { + // TODO(b/364360986): remove this before rolling the flag forward + Disambiguation(viewModel = statusBarViewModel) + + Row(Modifier.fillMaxSize()) { + val scope = rememberCoroutineScope() + AndroidView( + factory = { context -> + val inflater = LayoutInflater.from(context) + val phoneStatusBarView = + inflater.inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView + + // For now, just set up the system icons the same way we used to + val statusIconContainer = + phoneStatusBarView.requireViewById<StatusIconContainer>(R.id.statusIcons) + // TODO(b/364360986): turn this into a repo/intr/viewmodel + val darkIconManager = + darkIconManagerFactory.create(statusIconContainer, StatusBarLocation.HOME) + iconController.addIconGroup(darkIconManager) + + // TODO(b/372657935): This won't be needed once OngoingCallController is + // implemented in recommended architecture + ongoingCallController.setChipView( + phoneStatusBarView.requireViewById(R.id.ongoing_activity_chip_primary) + ) + + // For notifications, first inflate the [NotificationIconContainer] + val notificationIconArea = + phoneStatusBarView.requireViewById<ViewGroup>(R.id.notification_icon_area) + inflater.inflate(R.layout.notification_icon_area, notificationIconArea, true) + // Then bind it using the icons binder + val notificationIconContainer = + phoneStatusBarView.requireViewById<NotificationIconContainer>( + R.id.notificationIcons + ) + scope.launch { + notificationIconsBinder.bindWhileAttached(notificationIconContainer) + } + + // This binder handles everything else + scope.launch { + statusBarViewBinder.bind( + phoneStatusBarView, + statusBarViewModel, + nopVisibilityChangeListener, + ) + } + onViewCreated(phoneStatusBarView) + phoneStatusBarView + } + ) + } + } +} + +/** + * This is our analog of the flexi "ribbon", which just shows some text so we know if the flag is on + */ +@Composable +fun Disambiguation(viewModel: HomeStatusBarViewModel) { + val clockVisibilityModel = + viewModel.isClockVisible.collectAsStateWithLifecycle( + initialValue = + HomeStatusBarViewModel.VisibilityModel( + visibility = View.GONE, + shouldAnimateChange = false, + ) + ) + if (clockVisibilityModel.value.visibility == View.VISIBLE) { + Box(modifier = Modifier.fillMaxSize().alpha(0.5f), contentAlignment = Alignment.Center) { + RetroText(text = "COMPOSE->BAR") + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt index 366ea3516965..4277a8be64d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel import android.view.View import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING @@ -38,7 +39,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotif import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor import com.android.systemui.statusbar.pipeline.shared.domain.interactor.CollapsedStatusBarInteractor -import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel.VisibilityModel +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -61,7 +62,7 @@ import kotlinx.coroutines.flow.stateIn * [StatusBarHideIconsForBouncerManager]. We should move those pieces of logic to this class instead * so that it's all in one place and easily testable outside of the fragment. */ -interface CollapsedStatusBarViewModel { +interface HomeStatusBarViewModel { /** * True if the device is currently transitioning from lockscreen to occluded and false * otherwise. @@ -116,19 +117,20 @@ interface CollapsedStatusBarViewModel { } @SysUISingleton -class CollapsedStatusBarViewModelImpl +class HomeStatusBarViewModelImpl @Inject constructor( collapsedStatusBarInteractor: CollapsedStatusBarInteractor, private val lightsOutInteractor: LightsOutInteractor, private val notificationsInteractor: ActiveNotificationsInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, + keyguardInteractor: KeyguardInteractor, sceneInteractor: SceneInteractor, sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor, shadeInteractor: ShadeInteractor, ongoingActivityChipsViewModel: OngoingActivityChipsViewModel, @Application coroutineScope: CoroutineScope, -) : CollapsedStatusBarViewModel { +) : HomeStatusBarViewModel { override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> = keyguardTransitionInteractor .isInTransition(Edge.create(from = LOCKSCREEN, to = OCCLUDED)) @@ -184,29 +186,43 @@ constructor( // TODO(b/364360986): Add edge cases, like secure camera launch. } - private val isHomeScreenStatusBarAllowed: Flow<Boolean> = + private val isHomeStatusBarAllowed: Flow<Boolean> = if (SceneContainerFlag.isEnabled) { isHomeStatusBarAllowedByScene } else { isHomeScreenStatusBarAllowedLegacy } + private val shouldHomeStatusBarBeVisible = + combine(isHomeStatusBarAllowed, keyguardInteractor.isSecureCameraActive) { + isHomeStatusBarAllowed, + isSecureCameraActive -> + // When launching the camera over the lockscreen, the status icons would typically + // become visible momentarily before animating out, since we're not yet aware that the + // launching camera activity is fullscreen. Even once the activity finishes launching, + // it takes a short time before WM decides that the top app wants to hide the icons and + // tells us to hide them. + // To ensure that this high-visibility animation is smooth, keep the icons hidden during + // a camera launch. See b/257292822. + isHomeStatusBarAllowed && !isSecureCameraActive + } + override val isClockVisible: Flow<VisibilityModel> = combine( - isHomeScreenStatusBarAllowed, + shouldHomeStatusBarBeVisible, collapsedStatusBarInteractor.visibilityViaDisableFlags, - ) { isStatusBarAllowed, visibilityViaDisableFlags -> - val showClock = isStatusBarAllowed && visibilityViaDisableFlags.isClockAllowed + ) { shouldStatusBarBeVisible, visibilityViaDisableFlags -> + val showClock = shouldStatusBarBeVisible && visibilityViaDisableFlags.isClockAllowed // TODO(b/364360986): Take CollapsedStatusBarFragment.clockHiddenMode into account. VisibilityModel(showClock.toVisibilityInt(), visibilityViaDisableFlags.animate) } override val isNotificationIconContainerVisible: Flow<VisibilityModel> = combine( - isHomeScreenStatusBarAllowed, + shouldHomeStatusBarBeVisible, collapsedStatusBarInteractor.visibilityViaDisableFlags, - ) { isStatusBarAllowed, visibilityViaDisableFlags -> + ) { shouldStatusBarBeVisible, visibilityViaDisableFlags -> val showNotificationIconContainer = - isStatusBarAllowed && visibilityViaDisableFlags.areNotificationIconsAllowed + shouldStatusBarBeVisible && visibilityViaDisableFlags.areNotificationIconsAllowed VisibilityModel( showNotificationIconContainer.toVisibilityInt(), visibilityViaDisableFlags.animate, @@ -214,10 +230,11 @@ constructor( } override val isSystemInfoVisible: Flow<VisibilityModel> = combine( - isHomeScreenStatusBarAllowed, + shouldHomeStatusBarBeVisible, collapsedStatusBarInteractor.visibilityViaDisableFlags, - ) { isStatusBarAllowed, visibilityViaDisableFlags -> - val showSystemInfo = isStatusBarAllowed && visibilityViaDisableFlags.isSystemInfoAllowed + ) { shouldStatusBarBeVisible, visibilityViaDisableFlags -> + val showSystemInfo = + shouldStatusBarBeVisible && visibilityViaDisableFlags.isSystemInfoAllowed VisibilityModel(showSystemInfo.toVisibilityInt(), visibilityViaDisableFlags.animate) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt index e8dc93465685..ae0e76f01faa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt @@ -22,6 +22,7 @@ import android.view.ViewGroup import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.fragments.FragmentHostManager +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController import java.util.Optional /** Encapsulates all logic for the status bar window state management. */ @@ -80,6 +81,7 @@ interface StatusBarWindowController { fun create( context: Context, viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + statusBarConfigurationController: StatusBarConfigurationController, ): StatusBarWindowController } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java index d709e5a0cd6c..e4c6737856f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java @@ -54,6 +54,8 @@ import com.android.systemui.animation.DelegateTransitionAnimatorController; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.res.R; +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider; import com.android.systemui.statusbar.window.StatusBarWindowModule.InternalWindowViewInflater; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; @@ -74,13 +76,14 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController private final Context mContext; private final ViewCaptureAwareWindowManager mWindowManager; + private final StatusBarConfigurationController mStatusBarConfigurationController; private final IWindowManager mIWindowManager; private final StatusBarContentInsetsProvider mContentInsetsProvider; private int mBarHeight = -1; private final State mCurrentState = new State(); private boolean mIsAttached; - private final ViewGroup mStatusBarWindowView; + private final StatusBarWindowView mStatusBarWindowView; private final FragmentService mFragmentService; // The container in which we should run launch animations started from the status bar and // expanding into the opening window. @@ -94,12 +97,14 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController @Assisted Context context, @InternalWindowViewInflater StatusBarWindowViewInflater statusBarWindowViewInflater, @Assisted ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, + @Assisted StatusBarConfigurationController statusBarConfigurationController, IWindowManager iWindowManager, StatusBarContentInsetsProvider contentInsetsProvider, FragmentService fragmentService, Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) { mContext = context; mWindowManager = viewCaptureAwareWindowManager; + mStatusBarConfigurationController = statusBarConfigurationController; mIWindowManager = iWindowManager; mContentInsetsProvider = contentInsetsProvider; mStatusBarWindowView = statusBarWindowViewInflater.inflate(context); @@ -141,6 +146,10 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController @Override public void attach() { + if (StatusBarConnectedDisplays.isEnabled()) { + mStatusBarWindowView.setStatusBarConfigurationController( + mStatusBarConfigurationController); + } // Now that the status bar window encompasses the sliding panel and its // translucent backdrop, the entire thing is made TRANSLUCENT and is // hardware-accelerated. @@ -360,7 +369,8 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController @Override StatusBarWindowControllerImpl create( @NonNull Context context, - @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager); + @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, + @NonNull StatusBarConfigurationController statusBarConfigurationController); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt index 7d0dadcf8c6e..d83a2371ec92 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt @@ -17,78 +17,42 @@ package com.android.systemui.statusbar.window import android.content.Context -import android.view.Display import android.view.WindowManager import com.android.app.viewcapture.ViewCaptureAwareWindowManager -import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.repository.DisplayRepository import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository +import com.android.systemui.display.data.repository.PerDisplayStore +import com.android.systemui.display.data.repository.PerDisplayStoreImpl +import com.android.systemui.display.data.repository.SingleDisplayStore import com.android.systemui.statusbar.core.StatusBarConnectedDisplays -import java.util.concurrent.ConcurrentHashMap +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore import javax.inject.Inject -import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch /** Store that allows to retrieve per display instances of [StatusBarWindowController]. */ -interface StatusBarWindowControllerStore { - /** - * The instance for the default/main display of the device. For example, on a phone or a tablet, - * the default display is the internal/built-in display of the device. - * - * Note that the id of the default display is [Display.DEFAULT_DISPLAY]. - */ - val defaultDisplay: StatusBarWindowController - - /** - * Returns an instance for a specific display id. - * - * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing - * displays. - */ - fun forDisplay(displayId: Int): StatusBarWindowController -} +interface StatusBarWindowControllerStore : PerDisplayStore<StatusBarWindowController> @SysUISingleton class MultiDisplayStatusBarWindowControllerStore @Inject constructor( - @Background private val backgroundApplicationScope: CoroutineScope, + @Background backgroundApplicationScope: CoroutineScope, private val controllerFactory: StatusBarWindowController.Factory, private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory, - private val displayRepository: DisplayRepository, -) : StatusBarWindowControllerStore, CoreStartable { + private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, + displayRepository: DisplayRepository, +) : + StatusBarWindowControllerStore, + PerDisplayStoreImpl<StatusBarWindowController>(backgroundApplicationScope, displayRepository) { init { StatusBarConnectedDisplays.assertInNewMode() } - private val perDisplayControllers = ConcurrentHashMap<Int, StatusBarWindowController>() - - override fun start() { - backgroundApplicationScope.launch(CoroutineName("StatusBarWindowController#start")) { - displayRepository.displayRemovalEvent.collect { displayId -> - perDisplayControllers.remove(displayId) - } - } - } - - override val defaultDisplay: StatusBarWindowController - get() = forDisplay(Display.DEFAULT_DISPLAY) - - override fun forDisplay(displayId: Int): StatusBarWindowController { - if (displayRepository.getDisplay(displayId) == null) { - throw IllegalArgumentException("Display with id $displayId doesn't exist.") - } - return perDisplayControllers.computeIfAbsent(displayId) { - createControllerForDisplay(displayId) - } - } - - private fun createControllerForDisplay(displayId: Int): StatusBarWindowController { + override fun createInstanceForDisplay(displayId: Int): StatusBarWindowController { val statusBarDisplayContext = displayWindowPropertiesRepository.get( displayId = displayId, @@ -99,8 +63,11 @@ constructor( return controllerFactory.create( statusBarDisplayContext.context, viewCaptureAwareWindowManager, + statusBarConfigurationControllerStore.forDisplay(displayId), ) } + + override val instanceClass = StatusBarWindowController::class.java } @SysUISingleton @@ -110,16 +77,18 @@ constructor( context: Context, viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, factory: StatusBarWindowControllerImpl.Factory, -) : StatusBarWindowControllerStore { + statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, +) : + StatusBarWindowControllerStore, + PerDisplayStore<StatusBarWindowController> by SingleDisplayStore( + factory.create( + context, + viewCaptureAwareWindowManager, + statusBarConfigurationControllerStore.defaultDisplay, + ) + ) { init { StatusBarConnectedDisplays.assertInLegacyMode() } - - private val controller: StatusBarWindowController = - factory.create(context, viewCaptureAwareWindowManager) - - override val defaultDisplay = controller - - override fun forDisplay(displayId: Int) = controller } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java index d696979f1859..3f6ef16e2e5e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java @@ -22,6 +22,7 @@ import static android.view.MotionEvent.ACTION_UP; import static android.view.WindowInsets.Type.systemBars; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Insets; import android.util.AttributeSet; import android.view.DisplayCutout; @@ -30,8 +31,16 @@ import android.view.View; import android.view.WindowInsets; import android.widget.FrameLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.systemui.compose.ComposeInitializer; +import com.android.systemui.statusbar.core.StatusBarSimpleFragment; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; + /** * Status bar view. + * We now extend WindowRootView so that we can host Compose views */ public class StatusBarWindowView extends FrameLayout { @@ -44,12 +53,49 @@ public class StatusBarWindowView extends FrameLayout { private float mTouchDownY = 0; + @Nullable private StatusBarConfigurationController mConfigurationController; + public StatusBarWindowView(Context context, AttributeSet attrs) { super(context, attrs); setClipChildren(false); } @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (StatusBarSimpleFragment.isEnabled()) { + ComposeInitializer.INSTANCE.onAttachedToWindow(this); + } + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (StatusBarSimpleFragment.isEnabled()) { + ComposeInitializer.INSTANCE.onDetachedFromWindow(this); + } + } + + /** + * Sets the {@link StatusBarConfigurationController} that is associated with the display that + * this view is attached to. + */ + public void setStatusBarConfigurationController( + @NonNull StatusBarConfigurationController configurationController) { + mConfigurationController = configurationController; + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + StatusBarConfigurationController configurationController = mConfigurationController; + if (configurationController != null) { + configurationController.onConfigurationChanged(newConfig); + } + } + + @Override public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) { final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars()); mLeftInset = insets.left; @@ -89,8 +135,8 @@ public class StatusBarWindowView extends FrameLayout { final int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); - if (child.getLayoutParams() instanceof LayoutParams) { - LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (child.getLayoutParams() instanceof FrameLayout.LayoutParams) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams(); if (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset || lp.topMargin != mTopInset) { lp.rightMargin = mRightInset; diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt index 75c66f234bdc..90c005139c56 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt @@ -67,7 +67,7 @@ class DistanceBasedGestureRecognizerProvider( val distanceThresholdPx = resources.getDimensionPixelSize( com.android.internal.R.dimen.system_gestures_distance_threshold - ) + ) * 5 return remember(distanceThresholdPx) { recognizerFactory(distanceThresholdPx, gestureStateChangedCallback) } @@ -77,7 +77,8 @@ class DistanceBasedGestureRecognizerProvider( fun GestureState.toTutorialActionState(): TutorialActionState { return when (this) { NotStarted -> TutorialActionState.NotStarted - is InProgress -> TutorialActionState.InProgress(progress) + // progress is disabled for now as views are not ready to handle varying progress + is InProgress -> TutorialActionState.InProgress(0f) Finished -> TutorialActionState.Finished } } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt index 3c31efa6265a..c2093114c98f 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt @@ -39,11 +39,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import com.android.systemui.inputdevice.tutorial.ui.composable.DoneButton import com.android.systemui.res.R +import com.android.systemui.touchpad.tutorial.ui.gesture.isFourFingerTouchpadSwipe +import com.android.systemui.touchpad.tutorial.ui.gesture.isThreeFingerTouchpadSwipe @Composable fun TutorialSelectionScreen( @@ -55,7 +58,16 @@ fun TutorialSelectionScreen( Column( verticalArrangement = Arrangement.Center, modifier = - Modifier.background(color = MaterialTheme.colorScheme.surfaceContainer).fillMaxSize(), + Modifier.background(color = MaterialTheme.colorScheme.surfaceContainer) + .fillMaxSize() + .pointerInteropFilter( + onTouchEvent = { event -> + // Because of window flag we're intercepting 3 and 4-finger swipes. + // Although we don't handle them in this screen, we want to disable them so + // that user is not clicking button by mistake by performing these swipes. + isThreeFingerTouchpadSwipe(event) || isFourFingerTouchpadSwipe(event) + } + ), ) { TutorialSelectionButtons( onBackTutorialClicked = onBackTutorialClicked, diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt index 56e97a357d67..80f800390852 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt @@ -16,10 +16,14 @@ package com.android.systemui.touchpad.tutorial.ui.gesture +import android.util.MathUtils import android.view.MotionEvent import kotlin.math.abs -/** Recognizes touchpad back gesture, that is three fingers swiping left or right */ +/** + * Recognizes touchpad back gesture, that is - using three fingers on touchpad - swiping left or + * right. + */ class BackGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer { private val distanceTracker = DistanceTracker() @@ -36,7 +40,7 @@ class BackGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu gestureStateChangedCallback, gestureState, isFinished = { abs(it.deltaX) >= gestureDistanceThresholdPx }, - progress = { 0f }, + progress = { MathUtils.saturate(abs(it.deltaX / gestureDistanceThresholdPx)) }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt index 3db9d7ccc8f7..2b84a4c50613 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt @@ -16,9 +16,10 @@ package com.android.systemui.touchpad.tutorial.ui.gesture +import android.util.MathUtils import android.view.MotionEvent -/** Recognizes touchpad home gesture, that is three fingers swiping up */ +/** Recognizes touchpad home gesture, that is - using three fingers on touchpad - swiping up. */ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer { private val distanceTracker = DistanceTracker() @@ -35,7 +36,7 @@ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu gestureStateChangedCallback, gestureState, isFinished = { -it.deltaY >= gestureDistanceThresholdPx }, - progress = { 0f }, + progress = { MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx) }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt index a194ad6a8016..69b7c5edd750 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt @@ -16,13 +16,14 @@ package com.android.systemui.touchpad.tutorial.ui.gesture +import android.util.MathUtils import android.view.MotionEvent import kotlin.math.abs /** - * Recognizes apps gesture completion. That is - using three fingers on touchpad - swipe up over - * some distance threshold and then slow down gesture before fingers are lifted. Implementation is - * based on [com.android.quickstep.util.TriggerSwipeUpTouchTracker] + * Recognizes recent apps gesture, that is - using three fingers on touchpad - swipe up over some + * distance threshold and then slow down gesture before fingers are lifted. Implementation is based + * on [com.android.quickstep.util.TriggerSwipeUpTouchTracker] */ class RecentAppsGestureRecognizer( private val gestureDistanceThresholdPx: Int, @@ -49,7 +50,7 @@ class RecentAppsGestureRecognizer( -state.deltaY >= gestureDistanceThresholdPx && abs(velocityTracker.calculateVelocity().value) <= velocityThresholdPxPerMs }, - progress = { 0f }, + progress = { MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx) }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java index 213581787f73..d0817d736f07 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java @@ -61,7 +61,6 @@ public class TunerActivity extends Activity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight); getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); requestWindowFeature(Window.FEATURE_NO_TITLE); diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt index b3e60e35d89e..d4686e28ce5f 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt @@ -17,6 +17,7 @@ package com.android.systemui.unfold import android.content.Context +import android.hardware.devicestate.DeviceStateManager import android.util.Log import com.android.app.tracing.TraceUtils.traceAsync import com.android.app.tracing.instantForTrack @@ -72,7 +73,8 @@ constructor( @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor, @Application private val applicationScope: CoroutineScope, private val displaySwitchLatencyLogger: DisplaySwitchLatencyLogger, - private val systemClock: SystemClock + private val systemClock: SystemClock, + private val deviceStateManager: DeviceStateManager ) : CoreStartable { private val backgroundDispatcher = singleThreadBgExecutor.asCoroutineDispatcher() @@ -81,7 +83,7 @@ constructor( @OptIn(ExperimentalCoroutinesApi::class) override fun start() { - if (!isDeviceFoldable(context)) { + if (!isDeviceFoldable(context.resources, deviceStateManager)) { return } applicationScope.launch(backgroundDispatcher) { diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt index 33fa9b8e0062..f806a5c52d5a 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt @@ -27,6 +27,7 @@ import com.android.systemui.keyguard.ScreenLifecycle import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled import com.android.systemui.util.Compile +import com.android.systemui.util.Utils.isDeviceFoldable import java.util.Optional import java.util.concurrent.Executor import javax.inject.Inject @@ -51,18 +52,14 @@ constructor( @UiBackground private val uiBgExecutor: Executor, private val context: Context, private val contentResolver: ContentResolver, - private val screenLifecycle: ScreenLifecycle + private val screenLifecycle: ScreenLifecycle, ) : ScreenLifecycle.Observer, TransitionProgressListener { private var folded: Boolean? = null private var isTransitionEnabled: Boolean? = null private val foldStateListener = FoldStateListener(context) private var unfoldInProgress = false - private val isFoldable: Boolean - get() = - context.resources - .getIntArray(com.android.internal.R.array.config_foldedDeviceStates) - .isNotEmpty() + private val isFoldable: Boolean = isDeviceFoldable(context.resources, deviceStateManager) /** Registers for relevant events only if the device is foldable. */ fun init() { diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt index adf50a1e661b..a6224dcec13f 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt @@ -16,6 +16,7 @@ package com.android.systemui.unfold import android.content.Context +import android.hardware.devicestate.DeviceStateManager import android.os.Trace import com.android.app.tracing.TraceStateLogger import com.android.systemui.CoreStartable @@ -24,6 +25,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.unfold.data.repository.FoldStateRepository import com.android.systemui.unfold.system.DeviceStateRepository +import com.android.systemui.util.Utils.isDeviceFoldable import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope @@ -42,13 +44,10 @@ constructor( private val foldStateRepository: FoldStateRepository, @Application applicationScope: CoroutineScope, @Background private val coroutineContext: CoroutineContext, - private val deviceStateRepository: DeviceStateRepository + private val deviceStateRepository: DeviceStateRepository, + private val deviceStateManager: DeviceStateManager ) : CoreStartable { - private val isFoldable: Boolean - get() = - context.resources - .getIntArray(com.android.internal.R.array.config_foldedDeviceStates) - .isNotEmpty() + private val isFoldable: Boolean = isDeviceFoldable(context.resources, deviceStateManager) private val bgScope = applicationScope.plus(coroutineContext) diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java index 3953188bb828..800d2894f192 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java @@ -14,11 +14,17 @@ package com.android.systemui.util; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; + import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.hardware.devicestate.DeviceState; +import android.hardware.devicestate.DeviceStateManager; +import android.hardware.devicestate.feature.flags.Flags; import android.provider.Settings; import android.view.DisplayCutout; @@ -84,9 +90,23 @@ public class Utils { /** * Returns {@code true} if the device is a foldable device */ - public static boolean isDeviceFoldable(Context context) { - return context.getResources() - .getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0; + public static boolean isDeviceFoldable(Resources resources, + DeviceStateManager deviceStateManager) { + if (Flags.deviceStatePropertyMigration()) { + List<DeviceState> deviceStates = deviceStateManager.getSupportedDeviceStates(); + for (int i = 0; i < deviceStates.size(); i++) { + DeviceState state = deviceStates.get(i); + if (state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY) + || state.hasProperty( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) { + return true; + } + } + return false; + } else { + return resources.getIntArray( + com.android.internal.R.array.config_foldedDeviceStates).length != 0; + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt index de9231856e2c..d87607dec8a1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt +++ b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt @@ -11,6 +11,7 @@ import android.graphics.drawable.AnimatedStateListDrawable import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable import android.util.Log import androidx.annotation.Px import com.android.app.tracing.traceSection @@ -22,36 +23,35 @@ class DrawableSize { const val TAG = "SysUiDrawableSize" /** - * Downscales passed Drawable to set maximum width and height. This will only - * be done for Drawables that can be downscaled non-destructively - e.g. animated - * and stateful drawables will no be downscaled. + * Downscales passed Drawable to set maximum width and height. This will only be done for + * Drawables that can be downscaled non-destructively - e.g. animated drawables, stateful + * drawables, and drawables with mixed-type layers will not be downscaled. * - * Downscaling will keep the aspect ratio. - * This method will not touch drawables that already fit into size specification. + * Downscaling will keep the aspect ratio. This method will not touch drawables that already + * fit into size specification. * * @param resources Resources on which to base the density of resized drawable. * @param drawable Drawable to downscale. * @param maxWidth Maximum width of the downscaled drawable. * @param maxHeight Maximum height of the downscaled drawable. - * * @return returns downscaled drawable if it's possible to downscale it or original if it's - * not. + * not. */ @JvmStatic fun downscaleToSize( res: Resources, drawable: Drawable, @Px maxWidth: Int, - @Px maxHeight: Int + @Px maxHeight: Int, ): Drawable { traceSection("DrawableSize#downscaleToSize") { // Bitmap drawables can contain big bitmaps as their content while sneaking it past // us using density scaling. Inspect inside the Bitmap drawables for actual bitmap // size for those. - val originalWidth = (drawable as? BitmapDrawable)?.bitmap?.width - ?: drawable.intrinsicWidth - val originalHeight = (drawable as? BitmapDrawable)?.bitmap?.height - ?: drawable.intrinsicHeight + val originalWidth = + (drawable as? BitmapDrawable)?.bitmap?.width ?: drawable.intrinsicWidth + val originalHeight = + (drawable as? BitmapDrawable)?.bitmap?.height ?: drawable.intrinsicHeight // Don't touch drawable if we can't resolve sizes for whatever reason. if (originalWidth <= 0 || originalHeight <= 0) { @@ -61,14 +61,18 @@ class DrawableSize { // Do not touch drawables that are already within bounds. if (originalWidth < maxWidth && originalHeight < maxHeight) { if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Not resizing $originalWidth x $originalHeight" + " " + - "to $maxWidth x $maxHeight") + Log.d( + TAG, + "Not resizing $originalWidth x $originalHeight" + + " " + + "to $maxWidth x $maxHeight", + ) } return drawable } - if (!isSimpleBitmap(drawable)) { + if (isComplicatedBitmap(drawable)) { return drawable } @@ -80,19 +84,25 @@ class DrawableSize { val height = (originalHeight * scale).toInt() if (width <= 0 || height <= 0) { - Log.w(TAG, "Attempted to resize ${drawable.javaClass.simpleName} " + - "from $originalWidth x $originalHeight to invalid $width x $height.") + Log.w( + TAG, + "Attempted to resize ${drawable.javaClass.simpleName} " + + "from $originalWidth x $originalHeight to invalid $width x $height.", + ) return drawable } if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Resizing large drawable (${drawable.javaClass.simpleName}) " + - "from $originalWidth x $originalHeight to $width x $height") + Log.d( + TAG, + "Resizing large drawable (${drawable.javaClass.simpleName}) " + + "from $originalWidth x $originalHeight to $width x $height", + ) } // We want to keep existing config if it's more efficient than 32-bit RGB. - val config = (drawable as? BitmapDrawable)?.bitmap?.config - ?: Bitmap.Config.ARGB_8888 + val config = + (drawable as? BitmapDrawable)?.bitmap?.config ?: Bitmap.Config.ARGB_8888 val scaledDrawableBitmap = Bitmap.createBitmap(width, height, config) val canvas = Canvas(scaledDrawableBitmap) @@ -105,8 +115,8 @@ class DrawableSize { } } - private fun isSimpleBitmap(drawable: Drawable): Boolean { - return !(drawable.isStateful || isAnimated(drawable)) + private fun isComplicatedBitmap(drawable: Drawable): Boolean { + return drawable.isStateful || isAnimated(drawable) || hasComplicatedLayers(drawable) } private fun isAnimated(drawable: Drawable): Boolean { @@ -119,5 +129,30 @@ class DrawableSize { drawable is AnimatedStateListDrawable || drawable is AnimatedVectorDrawable } + + private fun hasComplicatedLayers(drawable: Drawable): Boolean { + if (drawable !is LayerDrawable) { + return false + } + if (drawable.numberOfLayers == 1) { + return false + } + + val firstLayerType = drawable.getDrawable(0).javaClass + for (i in 1..<drawable.numberOfLayers) { + val layer = drawable.getDrawable(i) + if (layer.javaClass != firstLayerType) { + // If different layers have different drawable types, we shouldn't scale it down + // because we may lose the level information if one of the layers is a bitmap + // and another layer is a level-list. See b/244282477. + return true + } + if (isComplicatedBitmap(layer)) { + return true + } + } + + return false + } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt index 2af84c7e46f0..579af73236f3 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt @@ -16,10 +16,9 @@ package com.android.systemui.util.kotlin -import com.android.app.tracing.coroutines.createCoroutineTracingContext +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.dagger.qualifiers.Tracing import dagger.Module import dagger.Provides import javax.inject.Singleton @@ -27,7 +26,6 @@ import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi /** Providers for various application-wide coroutines-related constructs. */ @Module @@ -35,16 +33,15 @@ class GlobalCoroutinesModule { @Provides @Singleton @Application - fun applicationScope( - @Main dispatcherContext: CoroutineContext, - ): CoroutineScope = CoroutineScope(dispatcherContext + createCoroutineTracingContext("ApplicationScope")) + fun applicationScope(@Main dispatcherContext: CoroutineContext): CoroutineScope = + CoroutineScope(dispatcherContext + newTracingContext("ApplicationScope")) @Provides @Singleton @Main @Deprecated( "Use @Main CoroutineContext instead", - ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext") + ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext"), ) fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt index 4d9aaa6dc6b0..ea8709f7d65c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt @@ -15,7 +15,6 @@ */ package com.android.systemui.util.settings -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.annotation.UserIdInt import android.content.ContentResolver import android.database.ContentObserver @@ -24,6 +23,7 @@ import android.provider.Settings.SettingNotFoundException import androidx.annotation.AnyThread import androidx.annotation.WorkerThread import com.android.app.tracing.TraceUtils.trace +import com.android.systemui.coroutines.newTracingContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -94,7 +94,7 @@ interface SettingsProxy { */ @AnyThread fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-A")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-A")).launch { registerContentObserverSync(getUriFor(name), settingsObserver) } @@ -109,9 +109,9 @@ interface SettingsProxy { fun registerContentObserverAsync( name: String, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-B")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-B")).launch { registerContentObserverSync(getUriFor(name), settingsObserver) registered.run() } @@ -144,7 +144,7 @@ interface SettingsProxy { */ @AnyThread fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-C")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-C")).launch { registerContentObserverSync(uri, settingsObserver) } @@ -159,9 +159,9 @@ interface SettingsProxy { fun registerContentObserverAsync( uri: Uri, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-D")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-D")).launch { registerContentObserverSync(uri, settingsObserver) registered.run() } @@ -175,7 +175,7 @@ interface SettingsProxy { fun registerContentObserverSync( name: String, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) /** @@ -204,9 +204,9 @@ interface SettingsProxy { fun registerContentObserverAsync( name: String, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-E")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-E")).launch { registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) } @@ -222,9 +222,9 @@ interface SettingsProxy { name: String, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-F")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-F")).launch { registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) registered.run() } @@ -273,9 +273,9 @@ interface SettingsProxy { fun registerContentObserverAsync( uri: Uri, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-G")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-G")).launch { registerContentObserverSync(uri, notifyForDescendants, settingsObserver) } @@ -291,9 +291,9 @@ interface SettingsProxy { uri: Uri, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-H")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-H")).launch { registerContentObserverSync(uri, notifyForDescendants, settingsObserver) registered.run() } @@ -330,7 +330,9 @@ interface SettingsProxy { */ @AnyThread fun unregisterContentObserverAsync(settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-I")).launch { unregisterContentObserver(settingsObserver) } + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-I")).launch { + unregisterContentObserver(settingsObserver) + } /** * Look up a name in the database. diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt index c820c07b61b1..c5deca214e28 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt @@ -15,7 +15,6 @@ */ package com.android.systemui.util.settings -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.annotation.UserIdInt import android.annotation.WorkerThread import android.content.ContentResolver @@ -24,6 +23,7 @@ import android.net.Uri import android.os.UserHandle import android.provider.Settings.SettingNotFoundException import com.android.app.tracing.TraceUtils.trace +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloat import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow @@ -79,7 +79,7 @@ interface UserSettingsProxy : SettingsProxy { } override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-A")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-A")).launch { registerContentObserverForUserSync(uri, settingsObserver, userId) } @@ -88,7 +88,7 @@ interface UserSettingsProxy : SettingsProxy { override fun registerContentObserverSync( uri: Uri, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) { registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId) } @@ -111,9 +111,9 @@ interface UserSettingsProxy : SettingsProxy { override fun registerContentObserverAsync( uri: Uri, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-B")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-B")).launch { registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId) } @@ -156,9 +156,9 @@ interface UserSettingsProxy : SettingsProxy { fun registerContentObserverForUserAsync( name: String, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-C")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-C")).launch { registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle) } @@ -197,9 +197,9 @@ interface UserSettingsProxy : SettingsProxy { fun registerContentObserverForUserAsync( uri: Uri, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-D")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-D")).launch { registerContentObserverForUserSync(uri, settingsObserver, userHandle) } @@ -214,9 +214,9 @@ interface UserSettingsProxy : SettingsProxy { uri: Uri, settingsObserver: ContentObserver, userHandle: Int, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-E")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-E")).launch { registerContentObserverForUserSync(uri, settingsObserver, userHandle) registered.run() } @@ -273,14 +273,14 @@ interface UserSettingsProxy : SettingsProxy { name: String, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) { - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-F")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-F")).launch { registerContentObserverForUserSync( getUriFor(name), notifyForDescendants, settingsObserver, - userHandle + userHandle, ) } } @@ -337,14 +337,14 @@ interface UserSettingsProxy : SettingsProxy { uri: Uri, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-G")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-G")).launch { registerContentObserverForUserSync( uri, notifyForDescendants, settingsObserver, - userHandle + userHandle, ) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt index 20d598a9334b..617aaa71d2d3 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt @@ -27,6 +27,8 @@ import com.android.settingslib.volume.data.repository.AudioRepositoryImpl import com.android.settingslib.volume.data.repository.AudioSharingRepository import com.android.settingslib.volume.data.repository.AudioSharingRepositoryEmptyImpl import com.android.settingslib.volume.data.repository.AudioSharingRepositoryImpl +import com.android.settingslib.volume.data.repository.AudioSystemRepository +import com.android.settingslib.volume.data.repository.AudioSystemRepositoryImpl import com.android.settingslib.volume.domain.interactor.AudioModeInteractor import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor import com.android.settingslib.volume.shared.AudioManagerEventsReceiver @@ -106,5 +108,11 @@ interface AudioModule { notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor, ): AudioVolumeInteractor = AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor) + + @Provides + @SysUISingleton + fun provideAudioSystemRepository( + @Application context: Context, + ): AudioSystemRepository = AudioSystemRepositoryImpl(context) } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt index 2dd0bdab93d1..5e0af634c786 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.volume.dialog.settings.domain import android.app.ActivityManager -import com.android.app.tracing.coroutines.flow.map +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.volume.Events import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog @@ -30,6 +30,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @VolumeDialogScope @@ -49,6 +50,7 @@ constructor( deviceProvisionedController.isCurrentUserSetup() && model.lockTaskModeState == ActivityManager.LOCK_TASK_MODE_NONE } + .flowName("VDSBI#isVisible") .stateIn(coroutineScope, SharingStarted.Eagerly, false) fun onButtonClicked() { diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt index 4be680ef66f1..1e4afc0ac5fe 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt @@ -17,8 +17,10 @@ package com.android.systemui.volume.panel.component.volume.domain.interactor import android.media.AudioManager +import com.android.settingslib.volume.data.repository.AudioSystemRepository import com.android.settingslib.volume.domain.interactor.AudioModeInteractor import com.android.settingslib.volume.shared.model.AudioStream +import com.android.systemui.Flags import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession import com.android.systemui.volume.panel.component.mediaoutput.shared.model.isTheSameSession @@ -41,6 +43,7 @@ constructor( @VolumePanelScope scope: CoroutineScope, mediaOutputInteractor: MediaOutputInteractor, audioModeInteractor: AudioModeInteractor, + private val audioSystemRepository: AudioSystemRepository, ) { val volumePanelSliders: StateFlow<List<SliderType>> = @@ -83,6 +86,16 @@ constructor( } private fun MutableList<SliderType>.addStream(stream: Int) { + // Hide other streams except STREAM_MUSIC if the isSingleVolume mode is on. This makes sure + // the volume slider in volume panel is consistent with the volume slider inside system + // settings app. + if (Flags.onlyShowMediaStreamSliderInSingleVolumeMode() && + audioSystemRepository.isSingleVolume && + stream != AudioManager.STREAM_MUSIC + ) { + return + } + add(SliderType.Stream(AudioStream(stream))) } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt index 3da725b9a51f..e590a7de09ad 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt @@ -22,6 +22,7 @@ import android.provider.Settings import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.unit.dp import com.android.internal.logging.UiEventLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -89,7 +90,7 @@ constructor( VolumePanelRoute.SETTINGS_VOLUME_PANEL -> activityStarter.startActivity( /* intent= */ Intent(Settings.Panel.ACTION_VOLUME), - /* dismissShade= */ true + /* dismissShade= */ true, ) VolumePanelRoute.SYSTEM_UI_VOLUME_PANEL -> volumePanelFactory.create(aboveStatusBar = true, view = null) @@ -122,6 +123,9 @@ constructor( remember(coroutineScope) { viewModelFactory.create(coroutineScope) } ) }, + isDraggable = false, + // TODO(b/337205027) change maxWidth + maxWidth = 800.dp, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt index 203e1da2afcf..efdd98d5498a 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt @@ -31,6 +31,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.data.repository.KeyguardClockRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.shared.Flags.ambientAod import com.android.systemui.user.data.model.SelectedUserModel import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.UserRepository @@ -144,14 +145,21 @@ constructor( override val wallpaperSupportsAmbientMode: StateFlow<Boolean> = wallpaperInfo .map { - // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode. - it?.supportsAmbientMode() == true + if (ambientAod()) { + // Force this mode for now, until ImageWallpaper supports it directly + // TODO(b/371236225) + true + } else { + // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient + // mode. + it?.supportsAmbientMode() == true + } } .stateIn( scope, // Always be listening for wallpaper changes. SharingStarted.Eagerly, - initialValue = wallpaperInfo.value?.supportsAmbientMode() == true, + initialValue = if (ambientAod()) true else false, ) override var rootView: View? = null diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt index 79ebf0128d02..fe6977c367b5 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt @@ -18,9 +18,13 @@ package com.android.systemui.wallpapers.domain.interactor import com.android.systemui.wallpapers.data.repository.WallpaperRepository import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow class WallpaperInteractor @Inject constructor(val wallpaperRepository: WallpaperRepository) { fun setNotificationStackAbsoluteBottom(bottom: Float) { wallpaperRepository.setNotificationStackAbsoluteBottom(bottom) } + + val wallpaperSupportsAmbientMode: StateFlow<Boolean> = + wallpaperRepository.wallpaperSupportsAmbientMode } diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt new file mode 100644 index 000000000000..a51acf66432a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wallpapers.ui.viewmodel + +import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor +import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow + +class WallpaperViewModel @Inject constructor(interactor: WallpaperInteractor) { + val wallpaperSupportsAmbientMode: StateFlow<Boolean> = interactor.wallpaperSupportsAmbientMode +} diff --git a/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml b/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml new file mode 100644 index 000000000000..d9ea0b91abdf --- /dev/null +++ b/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > + <item android:drawable="@drawable/ic_brightness"/> + <item android:drawable="@drawable/ic_brightness"/> +</layer-list> diff --git a/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml b/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml new file mode 100644 index 000000000000..796de8f15f47 --- /dev/null +++ b/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > + <!-- dessert_flan is a PNG while ic_brightness is a level-list. --> + <item android:drawable="@drawable/dessert_flan"/> + <item android:drawable="@drawable/ic_brightness"/> +</layer-list> diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java deleted file mode 100644 index c51aa04fc7b6..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; -import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE; -import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.drawable.AnimatedStateListDrawable; -import android.util.Pair; -import android.view.View; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityManager; -import android.widget.ImageView; - -import com.android.systemui.Flags; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.biometrics.AuthController; -import com.android.systemui.biometrics.AuthRippleController; -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; -import com.android.systemui.doze.util.BurnInHelperKt; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; -import com.android.systemui.kosmos.KosmosJavaAdapter; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.res.R; -import com.android.systemui.scene.shared.flag.SceneContainerFlag; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; - -import org.junit.After; -import org.junit.Before; -import org.mockito.Answers; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -public class LegacyLockIconViewControllerBaseTest extends SysuiTestCase { - protected static final String UNLOCKED_LABEL = "unlocked"; - protected static final String LOCKED_LABEL = "locked"; - protected static final int PADDING = 10; - - protected MockitoSession mStaticMockSession; - - protected final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); - protected @Mock DeviceEntryInteractor mDeviceEntryInteractor; - protected @Mock LockIconView mLockIconView; - protected @Mock ImageView mLockIcon; - protected @Mock AnimatedStateListDrawable mIconDrawable; - protected @Mock Context mContext; - protected @Mock Resources mResources; - protected @Mock(answer = Answers.RETURNS_DEEP_STUBS) WindowManager mWindowManager; - protected @Mock StatusBarStateController mStatusBarStateController; - protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; - protected @Mock KeyguardViewController mKeyguardViewController; - protected @Mock KeyguardStateController mKeyguardStateController; - protected @Mock FalsingManager mFalsingManager; - protected @Mock AuthController mAuthController; - protected @Mock DumpManager mDumpManager; - protected @Mock AccessibilityManager mAccessibilityManager; - protected @Mock ConfigurationController mConfigurationController; - protected @Mock VibratorHelper mVibrator; - protected @Mock AuthRippleController mAuthRippleController; - protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock()); - protected FakeFeatureFlags mFeatureFlags; - - protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; - - protected LegacyLockIconViewController mUnderTest; - - // Capture listeners so that they can be used to send events - @Captor protected ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor = - ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); - - @Captor protected ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateCaptor = - ArgumentCaptor.forClass(KeyguardStateController.Callback.class); - protected KeyguardStateController.Callback mKeyguardStateCallback; - - @Captor protected ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor = - ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); - protected StatusBarStateController.StateListener mStatusBarStateListener; - - @Captor protected ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor; - protected AuthController.Callback mAuthControllerCallback; - - @Captor protected ArgumentCaptor<KeyguardUpdateMonitorCallback> - mKeyguardUpdateMonitorCallbackCaptor = - ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); - protected KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; - - @Captor protected ArgumentCaptor<Point> mPointCaptor; - - @Before - public void setUp() throws Exception { - mStaticMockSession = mockitoSession() - .mockStatic(BurnInHelperKt.class) - .strictness(Strictness.LENIENT) - .startMocking(); - MockitoAnnotations.initMocks(this); - - setupLockIconViewMocks(); - when(mContext.getResources()).thenReturn(mResources); - when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager); - Rect windowBounds = new Rect(0, 0, 800, 1200); - when(mWindowManager.getCurrentWindowMetrics().getBounds()).thenReturn(windowBounds); - when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL); - when(mResources.getString(R.string.accessibility_lock_icon)).thenReturn(LOCKED_LABEL); - when(mResources.getDrawable(anyInt(), any())).thenReturn(mIconDrawable); - when(mResources.getDimensionPixelSize(R.dimen.lock_icon_padding)).thenReturn(PADDING); - when(mAuthController.getScaleFactor()).thenReturn(1f); - - when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); - when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); - - if (!SceneContainerFlag.isEnabled()) { - mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR); - //TODO move this to use @DisableFlags annotation if needed - mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); - } - - mFeatureFlags = new FakeFeatureFlags(); - mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); - mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false); - - mUnderTest = new LegacyLockIconViewController( - mStatusBarStateController, - mKeyguardUpdateMonitor, - mKeyguardViewController, - mKeyguardStateController, - mFalsingManager, - mAuthController, - mDumpManager, - mAccessibilityManager, - mConfigurationController, - mDelayableExecutor, - mVibrator, - mAuthRippleController, - mResources, - mKosmos.getKeyguardTransitionInteractor(), - KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(), - mFeatureFlags, - mPrimaryBouncerInteractor, - mContext, - () -> mDeviceEntryInteractor - ); - } - - @After - public void tearDown() { - mStaticMockSession.finishMocking(); - } - - protected Pair<Float, Point> setupUdfps() { - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); - final Point udfpsLocation = new Point(50, 75); - final float radius = 33f; - when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation); - when(mAuthController.getUdfpsRadius()).thenReturn(radius); - - return new Pair(radius, udfpsLocation); - } - - protected void setupShowLockIcon() { - when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); - when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarStateController.getDozeAmount()).thenReturn(0f); - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false); - } - - protected void captureAuthControllerCallback() { - verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture()); - mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue(); - } - - protected void captureKeyguardStateCallback() { - verify(mKeyguardStateController).addCallback(mKeyguardStateCaptor.capture()); - mKeyguardStateCallback = mKeyguardStateCaptor.getValue(); - } - - protected void captureStatusBarStateListener() { - verify(mStatusBarStateController).addCallback(mStatusBarStateCaptor.capture()); - mStatusBarStateListener = mStatusBarStateCaptor.getValue(); - } - - protected void captureKeyguardUpdateMonitorCallback() { - verify(mKeyguardUpdateMonitor).registerCallback( - mKeyguardUpdateMonitorCallbackCaptor.capture()); - mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue(); - } - - protected void setupLockIconViewMocks() { - when(mLockIconView.getResources()).thenReturn(mResources); - when(mLockIconView.getContext()).thenReturn(mContext); - when(mLockIconView.getLockIcon()).thenReturn(mLockIcon); - } - - protected void resetLockIconView() { - reset(mLockIconView); - setupLockIconViewMocks(); - } - - protected void init(boolean useDozeMigrationFlag) { - mFeatureFlags.set(DOZING_MIGRATION_1, useDozeMigrationFlag); - mUnderTest.setLockIconView(mLockIconView); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java deleted file mode 100644 index c1ba39e89cf9..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java +++ /dev/null @@ -1,414 +0,0 @@ -/* - * 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.keyguard; - -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; - -import static com.android.keyguard.LockIconView.ICON_LOCK; -import static com.android.keyguard.LockIconView.ICON_UNLOCK; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.graphics.Point; -import android.hardware.biometrics.BiometricSourceType; -import android.testing.TestableLooper; -import android.util.Pair; -import android.view.HapticFeedbackConstants; -import android.view.View; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.systemui.biometrics.UdfpsController; -import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; -import com.android.systemui.doze.util.BurnInHelperKt; -import com.android.systemui.flags.EnableSceneContainer; -import com.android.systemui.statusbar.StatusBarState; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) -@TestableLooper.RunWithLooper -public class LegacyLockIconViewControllerTest extends LegacyLockIconViewControllerBaseTest { - - @Override - public void setUp() throws Exception { - super.setUp(); - when(mLockIconView.isAttachedToWindow()).thenReturn(true); - } - - @Test - public void testUpdateFingerprintLocationOnInit() { - // GIVEN fp sensor location is available pre-attached - Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location - - // WHEN lock icon view controller is initialized and attached - init(/* useMigrationFlag= */false); - - // THEN lock icon view location is updated to the udfps location with UDFPS radius - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING)); - } - - @Test - public void testUpdatePaddingBasedOnResolutionScale() { - // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5 - Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location - when(mAuthController.getScaleFactor()).thenReturn(5f); - - // WHEN lock icon view controller is initialized and attached - init(/* useMigrationFlag= */false); - - // THEN lock icon view location is updated with the scaled radius - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING * 5)); - } - - @Test - public void testUpdateLockIconLocationOnAuthenticatorsRegistered() { - // GIVEN fp sensor location is not available pre-init - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); - when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); - init(/* useMigrationFlag= */false); - resetLockIconView(); // reset any method call counts for when we verify method calls later - - // GIVEN fp sensor location is available post-attached - captureAuthControllerCallback(); - Pair<Float, Point> udfps = setupUdfps(); - - // WHEN all authenticators are registered - mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT); - mDelayableExecutor.runAllReady(); - - // THEN lock icon view location is updated with the same coordinates as auth controller vals - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING)); - } - - @Test - public void testUpdateLockIconLocationOnUdfpsLocationChanged() { - // GIVEN fp sensor location is not available pre-init - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); - when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); - init(/* useMigrationFlag= */false); - resetLockIconView(); // reset any method call counts for when we verify method calls later - - // GIVEN fp sensor location is available post-attached - captureAuthControllerCallback(); - Pair<Float, Point> udfps = setupUdfps(); - - // WHEN udfps location changes - mAuthControllerCallback.onUdfpsLocationChanged(new UdfpsOverlayParams()); - mDelayableExecutor.runAllReady(); - - // THEN lock icon view location is updated with the same coordinates as auth controller vals - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING)); - } - - @Test - public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() { - // GIVEN Udpfs sensor location is available - setupUdfps(); - - // WHEN the view is attached - init(/* useMigrationFlag= */false); - - // THEN the lock icon view background should be enabled - verify(mLockIconView).setUseBackground(true); - } - - @Test - public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() { - // GIVEN Udfps sensor location is not supported - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); - - // WHEN the view is attached - init(/* useMigrationFlag= */false); - - // THEN the lock icon view background should be disabled - verify(mLockIconView).setUseBackground(false); - } - - @Test - public void testLockIconStartState() { - // GIVEN lock icon state - setupShowLockIcon(); - - // WHEN lock icon controller is initialized - init(/* useMigrationFlag= */false); - - // THEN the lock icon should show - verify(mLockIconView).updateIcon(ICON_LOCK, false); - } - - @Test - public void testLockIcon_updateToUnlock() { - // GIVEN starting state for the lock icon - setupShowLockIcon(); - - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureKeyguardStateCallback(); - reset(mLockIconView); - - // WHEN the unlocked state changes to canDismissLockScreen=true - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the unlock should show - verify(mLockIconView).updateIcon(ICON_UNLOCK, false); - } - - @Test - public void testLockIcon_clearsIconWhenUnlocked() { - // GIVEN udfps not enrolled - setupUdfps(); - when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false); - - // GIVEN starting state for the lock icon - setupShowLockIcon(); - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); - - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureStatusBarStateListener(); - reset(mLockIconView); - - // WHEN the dozing state changes - mStatusBarStateListener.onDozingChanged(false /* isDozing */); - - // THEN the icon is cleared - verify(mLockIconView).clearIcon(); - } - - @Test - public void testLockIcon_updateToAodLock_whenUdfpsEnrolled() { - // GIVEN udfps enrolled - setupUdfps(); - when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true); - - // GIVEN starting state for the lock icon - setupShowLockIcon(); - - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureStatusBarStateListener(); - reset(mLockIconView); - - // WHEN the dozing state changes - mStatusBarStateListener.onDozingChanged(true /* isDozing */); - - // THEN the AOD lock icon should show - verify(mLockIconView).updateIcon(ICON_LOCK, true); - } - - @Test - public void testBurnInOffsetsUpdated_onDozeAmountChanged() { - // GIVEN udfps enrolled - setupUdfps(); - when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true); - - // GIVEN burn-in offset = 5 - int burnInOffset = 5; - when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset); - - // GIVEN starting state for the lock icon (keyguard) - setupShowLockIcon(); - init(/* useMigrationFlag= */false); - captureStatusBarStateListener(); - reset(mLockIconView); - - // WHEN dozing updates - mStatusBarStateListener.onDozingChanged(true /* isDozing */); - mStatusBarStateListener.onDozeAmountChanged(1f, 1f); - - // THEN the view's translation is updated to use the AoD burn-in offsets - verify(mLockIconView).setTranslationY(burnInOffset); - verify(mLockIconView).setTranslationX(burnInOffset); - reset(mLockIconView); - - // WHEN the device is no longer dozing - mStatusBarStateListener.onDozingChanged(false /* isDozing */); - mStatusBarStateListener.onDozeAmountChanged(0f, 0f); - - // THEN the view is updated to NO translation (no burn-in offsets anymore) - verify(mLockIconView).setTranslationY(0); - verify(mLockIconView).setTranslationX(0); - } - - @Test - public void lockIconShows_afterUnlockStateChanges() { - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureKeyguardStateCallback(); - captureKeyguardUpdateMonitorCallback(); - - // GIVEN user has unlocked with a biometric auth (ie: face auth) - // and biometric running state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, - BiometricSourceType.FACE); - reset(mLockIconView); - - // WHEN the unlocked state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the lock icon is shown - verify(mLockIconView).setContentDescription(LOCKED_LABEL); - } - - @Test - public void lockIconAccessibility_notVisibleToUser() { - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureKeyguardStateCallback(); - captureKeyguardUpdateMonitorCallback(); - - // GIVEN user has unlocked with a biometric auth (ie: face auth) - // and biometric running state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, - BiometricSourceType.FACE); - reset(mLockIconView); - when(mLockIconView.isVisibleToUser()).thenReturn(false); - - // WHEN the unlocked state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the lock icon is shown - verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - } - - @Test - public void lockIconAccessibility_bouncerAnimatingAway() { - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureKeyguardStateCallback(); - captureKeyguardUpdateMonitorCallback(); - - // GIVEN user has unlocked with a biometric auth (ie: face auth) - // and biometric running state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, - BiometricSourceType.FACE); - reset(mLockIconView); - when(mLockIconView.isVisibleToUser()).thenReturn(true); - when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(true); - - // WHEN the unlocked state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the lock icon is shown - verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - } - - @Test - public void lockIconAccessibility_bouncerNotAnimatingAway_viewVisible() { - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureKeyguardStateCallback(); - captureKeyguardUpdateMonitorCallback(); - - // GIVEN user has unlocked with a biometric auth (ie: face auth) - // and biometric running state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, - BiometricSourceType.FACE); - reset(mLockIconView); - when(mLockIconView.isVisibleToUser()).thenReturn(true); - when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(false); - - // WHEN the unlocked state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the lock icon is shown - verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - - @Test - public void playHaptic_onTouchExploration_performHapticFeedback() { - // WHEN request to vibrate on touch exploration - mUnderTest.vibrateOnTouchExploration(); - - // THEN performHapticFeedback is used - verify(mVibrator).performHapticFeedback(any(), eq(HapticFeedbackConstants.CONTEXT_CLICK)); - } - - @Test - public void playHaptic_onLongPress_performHapticFeedback() { - // WHEN request to vibrate on long press - mUnderTest.vibrateOnLongPress(); - - // THEN uses perform haptic feedback - verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS)); - } - - @Test - public void longPress_showBouncer_sceneContainerNotEnabled() { - init(/* useMigrationFlag= */ false); - when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false); - - // WHEN longPress - mUnderTest.onLongPress(); - - // THEN show primary bouncer via keyguard view controller, not scene container - verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean()); - verify(mDeviceEntryInteractor, never()).attemptDeviceEntry(); - } - - @Test - @EnableSceneContainer - public void longPress_showBouncer() { - init(/* useMigrationFlag= */ false); - when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false); - - // WHEN longPress - mUnderTest.onLongPress(); - - // THEN show primary bouncer - verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean()); - verify(mDeviceEntryInteractor).attemptDeviceEntry(); - } - - @Test - @EnableSceneContainer - public void longPress_falsingTriggered_doesNotShowBouncer() { - init(/* useMigrationFlag= */ false); - when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true); - - // WHEN longPress - mUnderTest.onLongPress(); - - // THEN don't show primary bouncer - verify(mDeviceEntryInteractor, never()).attemptDeviceEntry(); - verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean()); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt deleted file mode 100644 index 2fd3cb0f0592..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard - -import android.view.View -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.keyguard.LockIconView.ICON_LOCK -import com.android.systemui.doze.util.getBurnInOffset -import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.util.mockito.whenever -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.anyBoolean -import org.mockito.Mockito.anyInt -import org.mockito.Mockito.reset -import org.mockito.Mockito.verify - -@RunWith(AndroidJUnit4::class) -@SmallTest -class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControllerBaseTest() { - - /** After migration, replaces LockIconViewControllerTest version */ - @Test - fun testLockIcon_clearsIconWhenUnlocked() = - runBlocking(IMMEDIATE) { - // GIVEN udfps not enrolled - setupUdfps() - whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false) - - // GIVEN starting state for the lock icon - setupShowLockIcon() - whenever(mStatusBarStateController.state).thenReturn(StatusBarState.SHADE) - - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */ true) - reset(mLockIconView) - - // WHEN the dozing state changes - mUnderTest.mIsDozingCallback.accept(false) - // THEN the icon is cleared - verify(mLockIconView).clearIcon() - } - - /** After migration, replaces LockIconViewControllerTest version */ - @Test - fun testLockIcon_updateToAodLock_whenUdfpsEnrolled() = - runBlocking(IMMEDIATE) { - // GIVEN udfps enrolled - setupUdfps() - whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true) - - // GIVEN starting state for the lock icon - setupShowLockIcon() - - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */ true) - reset(mLockIconView) - - // WHEN the dozing state changes - mUnderTest.mIsDozingCallback.accept(true) - - // THEN the AOD lock icon should show - verify(mLockIconView).updateIcon(ICON_LOCK, true) - } - - /** After migration, replaces LockIconViewControllerTest version */ - @Test - fun testBurnInOffsetsUpdated_onDozeAmountChanged() = - runBlocking(IMMEDIATE) { - // GIVEN udfps enrolled - setupUdfps() - whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true) - - // GIVEN burn-in offset = 5 - val burnInOffset = 5 - whenever(getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset) - - // GIVEN starting state for the lock icon (keyguard) - setupShowLockIcon() - init(/* useMigrationFlag= */ true) - reset(mLockIconView) - - // WHEN dozing updates - mUnderTest.mIsDozingCallback.accept(true) - mUnderTest.mDozeTransitionCallback.accept(1f) - - // THEN the view's translation is updated to use the AoD burn-in offsets - verify(mLockIconView).setTranslationY(burnInOffset.toFloat()) - verify(mLockIconView).setTranslationX(burnInOffset.toFloat()) - reset(mLockIconView) - - // WHEN the device is no longer dozing - mUnderTest.mIsDozingCallback.accept(false) - mUnderTest.mDozeTransitionCallback.accept(0f) - - // THEN the view is updated to NO translation (no burn-in offsets anymore) - verify(mLockIconView).setTranslationY(0f) - verify(mLockIconView).setTranslationX(0f) - } - - @Test - fun testHideLockIconView_onLockscreenHostedDreamStateChanged() = - runBlocking(IMMEDIATE) { - // GIVEN starting state for the lock icon (keyguard) and wallpaper dream enabled - mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true) - setupShowLockIcon() - init(/* useMigrationFlag= */ true) - reset(mLockIconView) - - // WHEN dream starts - mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept( - true /* isActiveDreamLockscreenHosted */ - ) - - // THEN the lock icon is hidden - verify(mLockIconView).visibility = View.INVISIBLE - reset(mLockIconView) - - // WHEN the device is no longer dreaming - mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept( - false /* isActiveDreamLockscreenHosted */ - ) - - // THEN lock icon is visible - verify(mLockIconView).visibility = View.VISIBLE - } - - companion object { - private val IMMEDIATE = Dispatchers.Main.immediate - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt index 8b427fbc5fb8..071acfa44650 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt @@ -156,7 +156,7 @@ class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() { createEndState(transitionContainer), backgroundLayer, fadeWindowBackgroundLayer, - useSpring, + useSpring = useSpring, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt deleted file mode 100644 index 6dc4b10a57da..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ /dev/null @@ -1,351 +0,0 @@ -/* - * 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.biometrics - -import android.graphics.Point -import android.hardware.biometrics.BiometricSourceType -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal -import android.testing.TestableLooper.RunWithLooper -import android.util.DisplayMetrics -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.keyguard.logging.KeyguardLogger -import com.android.systemui.Flags -import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION -import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository -import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor -import com.android.systemui.keyguard.WakefulnessLifecycle -import com.android.systemui.log.logcatLogBuffer -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.LightRevealScrim -import com.android.systemui.statusbar.NotificationShadeWindowController -import com.android.systemui.statusbar.commandline.CommandRegistry -import com.android.systemui.statusbar.phone.BiometricUnlockController -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.util.leak.RotationUtils -import com.android.systemui.util.mockito.any -import kotlinx.coroutines.ExperimentalCoroutinesApi -import org.junit.After -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers -import org.mockito.ArgumentMatchers.eq -import org.mockito.Captor -import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.reset -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations -import org.mockito.MockitoSession -import org.mockito.quality.Strictness -import javax.inject.Provider - - -@ExperimentalCoroutinesApi -@SmallTest -@RunWith(AndroidJUnit4::class) -class AuthRippleControllerTest : SysuiTestCase() { - private lateinit var staticMockSession: MockitoSession - - private lateinit var controller: AuthRippleController - @Mock private lateinit var rippleView: AuthRippleView - @Mock private lateinit var commandRegistry: CommandRegistry - @Mock private lateinit var configurationController: ConfigurationController - @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock private lateinit var authController: AuthController - @Mock private lateinit var authRippleInteractor: AuthRippleInteractor - @Mock private lateinit var keyguardStateController: KeyguardStateController - @Mock - private lateinit var wakefulnessLifecycle: WakefulnessLifecycle - @Mock - private lateinit var notificationShadeWindowController: NotificationShadeWindowController - @Mock - private lateinit var biometricUnlockController: BiometricUnlockController - @Mock - private lateinit var udfpsControllerProvider: Provider<UdfpsController> - @Mock - private lateinit var udfpsController: UdfpsController - @Mock - private lateinit var statusBarStateController: StatusBarStateController - @Mock - private lateinit var lightRevealScrim: LightRevealScrim - @Mock - private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal - - private val facePropertyRepository = FakeFacePropertyRepository() - private val displayMetrics = DisplayMetrics() - - @Captor - private lateinit var biometricUnlockListener: - ArgumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener> - - @Before - fun setUp() { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - MockitoAnnotations.initMocks(this) - staticMockSession = mockitoSession() - .mockStatic(RotationUtils::class.java) - .strictness(Strictness.LENIENT) - .startMocking() - - `when`(RotationUtils.getRotation(context)).thenReturn(RotationUtils.ROTATION_NONE) - `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp)) - `when`(udfpsControllerProvider.get()).thenReturn(udfpsController) - - controller = AuthRippleController( - context, - authController, - configurationController, - keyguardUpdateMonitor, - keyguardStateController, - wakefulnessLifecycle, - commandRegistry, - notificationShadeWindowController, - udfpsControllerProvider, - statusBarStateController, - displayMetrics, - KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)), - biometricUnlockController, - lightRevealScrim, - authRippleInteractor, - facePropertyRepository, - rippleView, - ) - controller.init() - } - - @After - fun tearDown() { - staticMockSession.finishMocking() - } - - @Test - fun testFingerprintTrigger_KeyguardShowing_Ripple() { - // GIVEN fp exists, keyguard is showing, unlocking with fp allowed - val fpsLocation = Point(5, 5) - `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) - controller.onViewAttached() - `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FINGERPRINT))).thenReturn(true) - - // WHEN fingerprint authenticated - verify(biometricUnlockController).addListener(biometricUnlockListener.capture()) - biometricUnlockListener.value - .onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType.FINGERPRINT) - - // THEN update sensor location and show ripple - verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f) - verify(rippleView).startUnlockedRipple(any()) - } - - @Test - fun testFingerprintTrigger_KeyguardNotShowing_NoRipple() { - // GIVEN fp exists & unlocking with fp allowed - val fpsLocation = Point(5, 5) - `when`(authController.udfpsLocation).thenReturn(fpsLocation) - controller.onViewAttached() - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FINGERPRINT))).thenReturn(true) - - // WHEN keyguard is NOT showing & fingerprint authenticated - `when`(keyguardStateController.isShowing).thenReturn(false) - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) - captor.value.onBiometricAuthenticated( - 0 /* userId */, - BiometricSourceType.FINGERPRINT /* type */, - false /* isStrongBiometric */) - - // THEN no ripple - verify(rippleView, never()).startUnlockedRipple(any()) - } - - @Test - fun testFingerprintTrigger_biometricUnlockNotAllowed_NoRipple() { - // GIVEN fp exists & keyguard is showing - val fpsLocation = Point(5, 5) - `when`(authController.udfpsLocation).thenReturn(fpsLocation) - controller.onViewAttached() - `when`(keyguardStateController.isShowing).thenReturn(true) - - // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FINGERPRINT))).thenReturn(false) - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) - captor.value.onBiometricAuthenticated( - 0 /* userId */, - BiometricSourceType.FINGERPRINT /* type */, - false /* isStrongBiometric */) - - // THEN no ripple - verify(rippleView, never()).startUnlockedRipple(any()) - } - - @Test - fun testNullFaceSensorLocationDoesNothing() { - facePropertyRepository.setSensorLocation(null) - controller.onViewAttached() - - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) - - captor.value.onBiometricAuthenticated( - 0 /* userId */, - BiometricSourceType.FACE /* type */, - false /* isStrongBiometric */) - verify(rippleView, never()).startUnlockedRipple(any()) - } - - @Test - fun testNullFingerprintSensorLocationDoesNothing() { - `when`(authController.fingerprintSensorLocation).thenReturn(null) - controller.onViewAttached() - - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) - - captor.value.onBiometricAuthenticated( - 0 /* userId */, - BiometricSourceType.FINGERPRINT /* type */, - false /* isStrongBiometric */) - verify(rippleView, never()).startUnlockedRipple(any()) - } - - @Test - fun registersAndDeregisters() { - controller.onViewAttached() - val captor = ArgumentCaptor - .forClass(KeyguardStateController.Callback::class.java) - verify(keyguardStateController).addCallback(captor.capture()) - val captor2 = ArgumentCaptor - .forClass(WakefulnessLifecycle.Observer::class.java) - verify(wakefulnessLifecycle).addObserver(captor2.capture()) - controller.onViewDetached() - verify(keyguardStateController).removeCallback(any()) - verify(wakefulnessLifecycle).removeObserver(any()) - } - - @Test - @RunWithLooper(setAsMainLooper = true) - fun testAnimatorRunWhenWakeAndUnlock_fingerprint() { - mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION) - val fpsLocation = Point(5, 5) - `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) - controller.onViewAttached() - `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - BiometricSourceType.FINGERPRINT)).thenReturn(true) - `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) - - controller.showUnlockRipple(BiometricSourceType.FINGERPRINT) - assertTrue("reveal didn't start on keyguardFadingAway", - controller.startLightRevealScrimOnKeyguardFadingAway) - `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true) - controller.onKeyguardFadingAwayChanged() - assertFalse("reveal triggers multiple times", - controller.startLightRevealScrimOnKeyguardFadingAway) - } - - @Test - @RunWithLooper(setAsMainLooper = true) - fun testAnimatorRunWhenWakeAndUnlock_faceUdfpsFingerDown() { - mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION) - val faceLocation = Point(5, 5) - facePropertyRepository.setSensorLocation(faceLocation) - controller.onViewAttached() - `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) - `when`(authController.isUdfpsFingerDown).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FACE))).thenReturn(true) - - controller.showUnlockRipple(BiometricSourceType.FACE) - assertTrue("reveal didn't start on keyguardFadingAway", - controller.startLightRevealScrimOnKeyguardFadingAway) - `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true) - controller.onKeyguardFadingAwayChanged() - assertFalse("reveal triggers multiple times", - controller.startLightRevealScrimOnKeyguardFadingAway) - } - - @Test - fun testUpdateRippleColor() { - controller.onViewAttached() - val captor = ArgumentCaptor - .forClass(ConfigurationController.ConfigurationListener::class.java) - verify(configurationController).addCallback(captor.capture()) - - reset(rippleView) - captor.value.onThemeChanged() - verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt()) - - reset(rippleView) - captor.value.onUiModeChanged() - verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt()) - } - - @Test - fun testUdfps_onFingerDown_runningForDeviceEntry_showDwellRipple() { - // GIVEN fingerprint detection is running on keyguard - `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true) - - // GIVEN view is already attached - controller.onViewAttached() - val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java) - verify(udfpsController).addCallback(captor.capture()) - - // GIVEN fp is updated to Point(5, 5) - val fpsLocation = Point(5, 5) - `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) - - // WHEN finger is down - captor.value.onFingerDown() - - // THEN update sensor location and show ripple - verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f) - verify(rippleView).startDwellRipple(false) - } - - @Test - fun testUdfps_onFingerDown_notDeviceEntry_doesNotShowDwellRipple() { - // GIVEN fingerprint detection is NOT running on keyguard - `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(false) - - // GIVEN view is already attached - controller.onViewAttached() - val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java) - verify(udfpsController).addCallback(captor.capture()) - - // WHEN finger is down - captor.value.onFingerDown() - - // THEN doesn't show dwell ripple - verify(rippleView, never()).startDwellRipple(false) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index e2a6a5508992..4baca713e19f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -16,21 +16,14 @@ package com.android.systemui.biometrics -import android.graphics.Rect import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS -import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING import android.hardware.biometrics.BiometricRequestConstants.RequestReason import android.hardware.fingerprint.IUdfpsOverlayControllerCallback import android.testing.TestableLooper.RunWithLooper import android.view.LayoutInflater import android.view.MotionEvent -import android.view.Surface -import android.view.Surface.Rotation import android.view.View -import android.view.ViewGroup import android.view.WindowManager import android.view.accessibility.AccessibilityManager import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -38,7 +31,6 @@ import androidx.test.filters.SmallTest import com.android.app.viewcapture.ViewCapture import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor @@ -61,9 +53,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState -import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController @@ -86,7 +76,6 @@ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever @@ -118,7 +107,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var dialogManager: SystemUIDialogManager @Mock private lateinit var dumpManager: DumpManager - @Mock private lateinit var transitionController: LockscreenShadeTransitionController @Mock private lateinit var configurationController: ConfigurationController @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock @@ -126,8 +114,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider @Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback @Mock private lateinit var udfpsController: UdfpsController - @Mock private lateinit var udfpsView: UdfpsView - @Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @@ -147,7 +133,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { private lateinit var powerInteractor: PowerInteractor private lateinit var testScope: TestScope - private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true } + private val onTouch = { _: View, _: MotionEvent -> true } private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams() private lateinit var controllerOverlay: UdfpsControllerOverlay @@ -158,53 +144,37 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { powerInteractor = kosmos.powerInteractor keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor - whenever(inflater.inflate(R.layout.udfps_view, null, false)).thenReturn(udfpsView) - whenever(inflater.inflate(R.layout.udfps_bp_view, null)) - .thenReturn(mock(UdfpsBpView::class.java)) - whenever(inflater.inflate(R.layout.udfps_keyguard_view_legacy, null)) - .thenReturn(mUdfpsKeyguardViewLegacy) - whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null)) - .thenReturn(mock(UdfpsFpmEmptyView::class.java)) } private suspend fun withReasonSuspend( @RequestReason reason: Int, isDebuggable: Boolean = false, - enableDeviceEntryUdfpsRefactor: Boolean = false, block: suspend () -> Unit, ) { - withReason( - reason, - isDebuggable, - enableDeviceEntryUdfpsRefactor, - ) + withReason(reason, isDebuggable) block() } private fun withReason( @RequestReason reason: Int, isDebuggable: Boolean = false, - enableDeviceEntryUdfpsRefactor: Boolean = false, block: () -> Unit = {}, ) { - if (enableDeviceEntryUdfpsRefactor) { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - } else { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - } controllerOverlay = UdfpsControllerOverlay( context, inflater, - ViewCaptureAwareWindowManager(windowManager, lazyViewCapture, - isViewCaptureEnabled = false), + ViewCaptureAwareWindowManager( + windowManager, + lazyViewCapture, + isViewCaptureEnabled = false, + ), accessibilityManager, statusBarStateController, statusBarKeyguardViewManager, keyguardUpdateMonitor, dialogManager, dumpManager, - transitionController, configurationController, keyguardStateController, unlockedScreenOffAnimationController, @@ -230,117 +200,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { block() } - @Test fun showUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { showUdfpsOverlay() } - - @Test - fun showUdfpsOverlay_keyguard() = - withReason(REASON_AUTH_KEYGUARD) { - showUdfpsOverlay() - verify(mUdfpsKeyguardViewLegacy).updateSensorLocation(eq(overlayParams.sensorBounds)) - } - - @Test fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() } - - private fun withRotation(@Rotation rotation: Int, block: () -> Unit) { - // Sensor that's in the top left corner of the display in natural orientation. - val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT) - val overlayBounds = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT) - overlayParams = - UdfpsOverlayParams( - sensorBounds, - overlayBounds, - DISPLAY_WIDTH, - DISPLAY_HEIGHT, - scaleFactor = 1f, - rotation - ) - block() - } - - @Test - fun showUdfpsOverlay_withRotation0() = - withRotation(Surface.ROTATION_0) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // ROTATION_0 is the native orientation. Sensor should stay in the top left corner. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_WIDTH) - assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT) - } - } - - @Test - fun showUdfpsOverlay_withRotation180() = - withRotation(Surface.ROTATION_180) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // ROTATION_180 is not supported. Sensor should stay in the top left corner. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_WIDTH) - assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT) - } - } - - @Test - fun showUdfpsOverlay_withRotation90() = - withRotation(Surface.ROTATION_90) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // Sensor should be in the bottom left corner in ROTATION_90. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT) - assertThat(lp.height).isEqualTo(DISPLAY_WIDTH) - } - } - - @Test - fun showUdfpsOverlay_withRotation270() = - withRotation(Surface.ROTATION_270) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // Sensor should be in the top right corner in ROTATION_270. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT) - assertThat(lp.height).isEqualTo(DISPLAY_WIDTH) - } - } - - @Test - fun showUdfpsOverlay_awake() = - testScope.runTest { - withReason(REASON_AUTH_KEYGUARD) { - powerRepository.updateWakefulness( - rawState = WakefulnessState.AWAKE, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - runCurrent() - controllerOverlay.show(udfpsController, overlayParams) - runCurrent() - verify(windowManager).addView(any(), any()) - } - } - @Test fun showUdfpsOverlay_whileGoingToSleep() = testScope.runTest { @@ -412,91 +271,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } @Test - fun showUdfpsOverlay_afterFinishedTransitioningToAOD() = - testScope.runTest { - withReasonSuspend(REASON_AUTH_KEYGUARD) { - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.OFF, - to = KeyguardState.GONE, - testScope = this, - ) - powerRepository.updateWakefulness( - rawState = WakefulnessState.STARTING_TO_SLEEP, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - runCurrent() - - // WHEN a request comes to show the view - controllerOverlay.show(udfpsController, overlayParams) - runCurrent() - - // THEN the view does not get added immediately - verify(windowManager, never()).addView(any(), any()) - - // WHEN the device finishes transitioning to AOD - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - testScope = this, - ) - runCurrent() - - // THEN the view gets added - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - } - } - - private fun showUdfpsOverlay() { - val didShow = controllerOverlay.show(udfpsController, overlayParams) - - verify(windowManager).addView(eq(controllerOverlay.getTouchOverlay()), any()) - verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode)) - verify(udfpsView).animationViewController = any() - verify(udfpsView).addView(any()) - - assertThat(didShow).isTrue() - assertThat(controllerOverlay.isShowing).isTrue() - assertThat(controllerOverlay.isHiding).isFalse() - assertThat(controllerOverlay.getTouchOverlay()).isNotNull() - } - - @Test fun hideUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { hideUdfpsOverlay() } - - @Test fun hideUdfpsOverlay_keyguard() = withReason(REASON_AUTH_KEYGUARD) { hideUdfpsOverlay() } - - @Test fun hideUdfpsOverlay_settings() = withReason(REASON_AUTH_SETTINGS) { hideUdfpsOverlay() } - - @Test fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() } - - private fun hideUdfpsOverlay() { - val didShow = controllerOverlay.show(udfpsController, overlayParams) - val view = controllerOverlay.getTouchOverlay() - view?.let { whenever(view.parent).thenReturn(mock(ViewGroup::class.java)) } - val didHide = controllerOverlay.hide() - - verify(windowManager).removeView(eq(view)) - - assertThat(didShow).isTrue() - assertThat(didHide).isTrue() - assertThat(controllerOverlay.getTouchOverlay()).isNull() - assertThat(controllerOverlay.animationViewController).isNull() - assertThat(controllerOverlay.isShowing).isFalse() - assertThat(controllerOverlay.isHiding).isTrue() - } - - @Test fun canNotHide() = withReason(REASON_AUTH_BP) { assertThat(controllerOverlay.hide()).isFalse() } @Test - fun canNotReshow() = - withReason(REASON_AUTH_BP) { - assertThat(controllerOverlay.show(udfpsController, overlayParams)).isTrue() - assertThat(controllerOverlay.show(udfpsController, overlayParams)).isFalse() - } - - @Test fun cancels() = withReason(REASON_AUTH_BP) { controllerOverlay.cancel() @@ -504,16 +281,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } @Test - fun unconfigureDisplayOnHide() = - withReason(REASON_AUTH_BP) { - whenever(udfpsView.isDisplayConfigured).thenReturn(true) - - controllerOverlay.show(udfpsController, overlayParams) - controllerOverlay.hide() - verify(udfpsView).unconfigureDisplay() - } - - @Test fun matchesRequestIds() = withReason(REASON_AUTH_BP) { assertThat(controllerOverlay.matchesRequestId(REQUEST_ID)).isTrue() @@ -521,29 +288,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } @Test - fun smallOverlayOnEnrollmentWithA11y() = - withRotation(Surface.ROTATION_0) { - withReason(REASON_ENROLL_ENROLLING) { - // When a11y enabled during enrollment - whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(true) - - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // Layout params should use sensor bounds - val lp = layoutParamsCaptor.value - assertThat(lp.width).isEqualTo(overlayParams.sensorBounds.width()) - assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height()) - } - } - - @Test fun addViewPending_layoutIsNotUpdated() = testScope.runTest { withReasonSuspend(REASON_AUTH_KEYGUARD) { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - // GIVEN going to sleep keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.OFF, @@ -574,26 +321,4 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { controllerOverlay.hide() } } - - @Test - fun updateOverlayParams_viewLayoutUpdated() = - testScope.runTest { - withReasonSuspend(REASON_AUTH_KEYGUARD) { - powerRepository.updateWakefulness( - rawState = WakefulnessState.AWAKE, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - runCurrent() - controllerOverlay.show(udfpsController, overlayParams) - runCurrent() - verify(windowManager).addView(any(), any()) - - // WHEN updateOverlayParams gets called - controllerOverlay.updateOverlayParams(overlayParams) - - // THEN the view layout is updated - verify(windowManager).updateViewLayout(any(), any()) - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt deleted file mode 100644 index 9fbe09619ff1..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.biometrics - -import android.hardware.biometrics.SensorLocationInternal -import android.testing.TestableLooper -import android.testing.ViewUtils -import android.view.LayoutInflater -import android.view.Surface -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.res.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.withArgCaptor -import com.google.common.truth.Truth.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.nullable -import org.mockito.Mockito.verify -import org.mockito.junit.MockitoJUnit - -private const val SENSOR_X = 50 -private const val SENSOR_Y = 250 -private const val SENSOR_RADIUS = 10 - -@SmallTest -@RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper -class UdfpsViewTest : SysuiTestCase() { - - @JvmField @Rule - var rule = MockitoJUnit.rule() - - @Mock - lateinit var hbmProvider: UdfpsDisplayModeProvider - @Mock - lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView> - - private lateinit var view: UdfpsView - - @Before - fun setup() { - context.setTheme(androidx.appcompat.R.style.Theme_AppCompat) - view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView - view.animationViewController = animationViewController - val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect - view.overlayParams = UdfpsOverlayParams(sensorBounds, sensorBounds, 1920, - 1080, 1f, Surface.ROTATION_0) - view.setUdfpsDisplayModeProvider(hbmProvider) - ViewUtils.attachView(view) - } - - @After - fun cleanup() { - ViewUtils.detachView(view) - } - - // TODO: Add test to verify view is size of screen - - @Test - fun startAndStopIllumination() { - val onDone: Runnable = mock() - view.configureDisplay(onDone) - - val illuminator = withArgCaptor<Runnable> { - verify(hbmProvider).enable(capture()) - } - - assertThat(view.isDisplayConfigured).isTrue() - verify(animationViewController).onDisplayConfiguring() - verify(animationViewController, never()).onDisplayUnconfigured() - verify(onDone, never()).run() - - // fake illumination event - illuminator.run() - waitForLooper() - verify(onDone).run() - verify(hbmProvider, never()).disable(any()) - - view.unconfigureDisplay() - assertThat(view.isDisplayConfigured).isFalse() - verify(animationViewController).onDisplayUnconfigured() - verify(hbmProvider).disable(nullable(Runnable::class.java)) - } - - private fun waitForLooper() = TestableLooper.get(this).processAllMessages() -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index b87647ef9839..eb72f29046aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -48,11 +48,11 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.res.Configuration; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.AndroidTestingRunner; -import android.testing.UiThreadTest; +import androidx.test.annotation.UiThreadTest; import android.view.Display; import androidx.annotation.NonNull; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -69,7 +69,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @UiThreadTest public class DozeMachineTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt index 5b216620ec2b..cb2c8fc2c418 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt @@ -114,9 +114,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { private lateinit var dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel @Mock - private lateinit var dreamingHostedToLockscreenTransitionViewModel: - DreamingHostedToLockscreenTransitionViewModel - @Mock private lateinit var dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel @Mock @@ -135,9 +132,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { private lateinit var lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel @Mock - private lateinit var lockscreenToDreamingHostedTransitionViewModel: - LockscreenToDreamingHostedTransitionViewModel - @Mock private lateinit var lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel @Mock @@ -182,8 +176,8 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS, KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END + ":" + - BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET - ) + BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET, + ), ) homeControlsQuickAffordanceConfig = @@ -263,8 +257,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha) .thenReturn(intendedAlphaMutableStateFlow) whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) - whenever(dreamingHostedToLockscreenTransitionViewModel.shortcutsAlpha) - .thenReturn(emptyFlow()) whenever(dreamingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) whenever(goneToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) whenever(occludedToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) @@ -274,8 +266,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { whenever(lockscreenToAodTransitionViewModel.shortcutsAlpha) .thenReturn(intendedAlphaMutableStateFlow) whenever(lockscreenToDozingTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) - whenever(lockscreenToDreamingHostedTransitionViewModel.shortcutsAlpha) - .thenReturn(emptyFlow()) whenever(lockscreenToDreamingTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) whenever(lockscreenToGoneTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) whenever(lockscreenToOccludedTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) @@ -314,8 +304,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { shadeInteractor = shadeInteractor, aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel, dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel, - dreamingHostedToLockscreenTransitionViewModel = - dreamingHostedToLockscreenTransitionViewModel, dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel, goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel, occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel, @@ -326,8 +314,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { glanceableHubToLockscreenTransitionViewModel, lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel, lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel, - lockscreenToDreamingHostedTransitionViewModel = - lockscreenToDreamingHostedTransitionViewModel, lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel, lockscreenToGoneTransitionViewModel = lockscreenToGoneTransitionViewModel, lockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel, @@ -683,8 +669,8 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { min( 1f, KeyguardQuickAffordancesCombinedViewModel - .AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD + 0.1f - ), + .AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD + 0.1f, + ) ) val testConfig = @@ -779,7 +765,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { testScope.runTest { kosmos.setTransition( sceneTransition = Idle(Scenes.Lockscreen), - stateTransition = TransitionStep(from = AOD, to = LOCKSCREEN) + stateTransition = TransitionStep(from = AOD, to = LOCKSCREEN), ) intendedShadeAlphaMutableStateFlow.value = 0.25f val underTest = collectLastValue(underTest.transitionAlpha) @@ -794,7 +780,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { testScope.runTest { kosmos.setTransition( sceneTransition = Idle(Scenes.Gone), - stateTransition = TransitionStep(from = AOD, to = GONE) + stateTransition = TransitionStep(from = AOD, to = GONE), ) intendedShadeAlphaMutableStateFlow.value = 0.5f val underTest = collectLastValue(underTest.transitionAlpha) @@ -829,7 +815,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { when (testConfig.isActivated) { true -> ActivationState.Active false -> ActivationState.Inactive - } + }, ) } else { KeyguardQuickAffordanceConfig.LockScreenState.Hidden @@ -878,7 +864,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { val intent: Intent? = null, val isSelected: Boolean = false, val isDimmed: Boolean = false, - val slotId: String = "" + val slotId: String = "", ) { init { check(!isVisible || icon != null) { "Must supply non-null icon if visible!" } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java index 413aa55f4554..bc3c0d96fa22 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java @@ -40,6 +40,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import android.hardware.devicestate.DeviceStateManager; import android.util.SparseArray; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -94,6 +95,8 @@ public class NavigationBarControllerImplTest extends SysuiTestCase { private NavigationBarComponent.Factory mNavigationBarFactory; @Mock TaskbarDelegate mTaskbarDelegate; + @Mock + private DeviceStateManager mDeviceStateManager; @Before public void setUp() { @@ -116,7 +119,8 @@ public class NavigationBarControllerImplTest extends SysuiTestCase { Optional.of(mock(Pip.class)), Optional.of(mock(BackAnimation.class)), mock(SecureSettings.class), - mDisplayTracker)); + mDisplayTracker, + mDeviceStateManager)); initializeNavigationBars(); mMockitoSession = mockitoSession().mockStatic(Utilities.class).startMocking(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt index ae2a9add4fb7..6fce108f35a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt @@ -20,10 +20,10 @@ import android.animation.AnimatorTestRule import android.content.Context import android.service.quicksettings.Tile import android.testing.AndroidTestingRunner -import android.testing.UiThreadTest import android.view.ContextThemeWrapper import android.view.View import android.widget.ImageView +import androidx.test.annotation.UiThreadTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.qs.QSTile diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java index 643debfbc810..7b24233d8603 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java @@ -17,13 +17,13 @@ import android.os.Handler; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.testing.TestableLooper; -import android.testing.UiThreadTest; import android.view.View; import android.view.Window; import android.widget.LinearLayout; import android.widget.Switch; import android.widget.TextView; +import androidx.test.annotation.UiThreadTest; import androidx.recyclerview.widget.RecyclerView; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java index 3aaaf95810a4..83ede465c885 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java @@ -16,6 +16,11 @@ package com.android.systemui.reardisplay; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotSame; @@ -53,6 +58,9 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.List; +import java.util.Set; + @SmallTest @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @@ -69,13 +77,25 @@ public class RearDisplayDialogControllerTest extends SysuiTestCase { private SysUiState mSysUiState; @Mock private Resources mResources; + @Mock + private DeviceStateManager mDeviceStateManager; LayoutInflater mLayoutInflater = LayoutInflater.from(mContext); private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); - private static final int CLOSED_BASE_STATE = 0; - private static final int OPEN_BASE_STATE = 1; + private static final DeviceState CLOSED_BASE_STATE = new DeviceState( + new DeviceState.Configuration.Builder(0, "CLOSED").setSystemProperties( + Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)) + .setPhysicalProperties(Set.of( + PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)) + .build()); + private static final DeviceState OPEN_BASE_STATE = new DeviceState( + new DeviceState.Configuration.Builder(1, "OPEN").setSystemProperties( + Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) + .setPhysicalProperties(Set.of( + PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)) + .build()); @Before public void setup() { @@ -92,12 +112,13 @@ public class RearDisplayDialogControllerTest extends SysuiTestCase { mFakeExecutor, mResources, mLayoutInflater, - mSystemUIDialogFactory); + mSystemUIDialogFactory, + mDeviceStateManager); controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback()); - controller.setFoldedStates(new int[]{0}); + controller.setFoldedStates(List.of(0)); controller.setAnimationRepeatCount(0); - controller.showRearDisplayDialog(CLOSED_BASE_STATE); + controller.showRearDisplayDialog(CLOSED_BASE_STATE.getIdentifier()); verify(mSystemUIDialog).show(); View container = getDialogViewContainer(); @@ -115,12 +136,13 @@ public class RearDisplayDialogControllerTest extends SysuiTestCase { mFakeExecutor, mResources, mLayoutInflater, - mSystemUIDialogFactory); + mSystemUIDialogFactory, + mDeviceStateManager); controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback()); - controller.setFoldedStates(new int[]{0}); + controller.setFoldedStates(List.of(0)); controller.setAnimationRepeatCount(0); - controller.showRearDisplayDialog(CLOSED_BASE_STATE); + controller.showRearDisplayDialog(CLOSED_BASE_STATE.getIdentifier()); verify(mSystemUIDialog).show(); View container = getDialogViewContainer(); TextView deviceClosedTitleTextView = container.findViewById( @@ -144,12 +166,13 @@ public class RearDisplayDialogControllerTest extends SysuiTestCase { mFakeExecutor, mResources, mLayoutInflater, - mSystemUIDialogFactory); + mSystemUIDialogFactory, + mDeviceStateManager); controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback()); - controller.setFoldedStates(new int[]{0}); + controller.setFoldedStates(List.of(0)); controller.setAnimationRepeatCount(0); - controller.showRearDisplayDialog(OPEN_BASE_STATE); + controller.showRearDisplayDialog(OPEN_BASE_STATE.getIdentifier()); verify(mSystemUIDialog).show(); View container = getDialogViewContainer(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt index a68ba06637cc..e2f22cde96de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt @@ -17,20 +17,16 @@ package com.android.systemui.statusbar import android.testing.TestableLooper -import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.times -import org.mockito.Mockito.verify import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @@ -38,36 +34,6 @@ import org.mockito.kotlin.whenever @TestableLooper.RunWithLooper(setAsMainLooper = true) class KeyguardIndicationControllerWithCoroutinesTest : KeyguardIndicationControllerBaseTest() { @Test - fun testIndicationAreaVisibility_onLockscreenHostedDreamStateChanged() = - runBlocking(IMMEDIATE) { - // GIVEN starting state for keyguard indication and wallpaper dream enabled - createController() - mFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true) - mController.setVisible(true) - - // THEN indication area is visible - verify(mIndicationArea, times(2)).visibility = View.VISIBLE - - // WHEN the device is dreaming with lockscreen hosted dream - mController.mIsActiveDreamLockscreenHostedCallback.accept( - true /* isActiveDreamLockscreenHosted */ - ) - mExecutor.runAllReady() - - // THEN the indication area is hidden - verify(mIndicationArea).visibility = View.GONE - - // WHEN the device stops dreaming with lockscreen hosted dream - mController.mIsActiveDreamLockscreenHostedCallback.accept( - false /* isActiveDreamLockscreenHosted */ - ) - mExecutor.runAllReady() - - // THEN indication area is set visible - verify(mIndicationArea, times(3)).visibility = View.VISIBLE - } - - @Test fun onTrustAgentErrorMessageDelayed_fingerprintEngaged() { createController() mController.setVisible(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt deleted file mode 100644 index 0d1d37af7e5b..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.core - -import android.platform.test.annotations.EnableFlags -import android.view.Display -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.display.data.repository.displayRepository -import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.testScope -import com.android.systemui.kosmos.unconfinedTestDispatcher -import com.android.systemui.testKosmos -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) -class MultiDisplayStatusBarInitializerStoreTest : SysuiTestCase() { - - private val kosmos = - testKosmos().also { - // Using unconfinedTestDispatcher to avoid having to call `runCurrent` in the tests. - it.testDispatcher = it.unconfinedTestDispatcher - } - private val testScope = kosmos.testScope - private val fakeDisplayRepository = kosmos.displayRepository - private val store = kosmos.multiDisplayStatusBarInitializerStore - - @Before - fun start() { - store.start() - } - - @Before - fun addDisplays() = runBlocking { - fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY_ID) - fakeDisplayRepository.addDisplay(NON_DEFAULT_DISPLAY_ID) - } - - @Test - fun forDisplay_defaultDisplay_multipleCalls_returnsSameInstance() = - testScope.runTest { - val controller = store.defaultDisplay - - assertThat(store.defaultDisplay).isSameInstanceAs(controller) - } - - @Test - fun forDisplay_nonDefaultDisplay_multipleCalls_returnsSameInstance() = - testScope.runTest { - val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID) - - assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(controller) - } - - @Test - fun forDisplay_nonDefaultDisplay_afterDisplayRemoved_returnsNewInstance() = - testScope.runTest { - val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID) - - fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID) - fakeDisplayRepository.addDisplay(NON_DEFAULT_DISPLAY_ID) - - assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(controller) - } - - @Test(expected = IllegalArgumentException::class) - fun forDisplay_nonExistingDisplayId_throws() = - testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) } - - companion object { - private const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY - private const val NON_DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1 - private const val NON_EXISTING_DISPLAY_ID = Display.DEFAULT_DISPLAY + 2 - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java index 2d8e69280d30..c8ef663a4f81 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java @@ -31,12 +31,12 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.testing.AndroidTestingRunner; -import android.testing.UiThreadTest; import android.util.FloatProperty; import android.util.Property; import android.view.View; import android.view.animation.Interpolator; +import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.app.animation.Interpolators; @@ -270,4 +270,4 @@ public class PropertyAnimatorTest extends SysuiTestCase { PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties); assertTrue(PropertyAnimator.isAnimating(mView, mProperty)); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index ed7383cacd29..e3e2491e5d52 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -612,6 +612,8 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { Assert.assertNull(child.getParent()); Assert.assertNull(child.getNotificationParent()); Assert.assertFalse(child.keepInParentForDismissAnimation()); + verify(mNotificationTestHelper.getMockLogger()) + .logCancelAppearDrawing(child.getEntry(), false); verifyNoMoreInteractions(mNotificationTestHelper.getMockLogger()); } @@ -1013,7 +1015,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { assertThat(row.isHeadsUpAnimatingAway()).isTrue(); // on disappear animation ends - row.onAppearAnimationFinished(/* wasAppearing = */ false); + row.onAppearAnimationFinished(/* wasAppearing = */ false, /* cancelled = */ false); assertThat(row.isHeadsUpAnimatingAway()).isFalse(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 40b8cdafa813..1e88215bf339 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -18,10 +18,15 @@ package com.android.systemui.statusbar.phone; import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN; +import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP; +import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE; import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED; import static android.provider.Settings.Global.HEADS_UP_ON; -import static com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR; import static com.android.systemui.Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE; import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION; import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT; @@ -124,7 +129,6 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.notetask.NoteTaskController; @@ -187,6 +191,8 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent; +import com.android.systemui.statusbar.pipeline.shared.ui.composable.StatusBarRootFactory; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -224,7 +230,9 @@ import org.mockito.MockitoAnnotations; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; +import java.util.List; import java.util.Optional; +import java.util.Set; import javax.inject.Provider; @@ -234,8 +242,22 @@ import javax.inject.Provider; @EnableFlags(FLAG_LIGHT_REVEAL_MIGRATION) public class CentralSurfacesImplTest extends SysuiTestCase { - private static final int FOLD_STATE_FOLDED = 0; - private static final int FOLD_STATE_UNFOLDED = 1; + private static final DeviceState FOLD_STATE_FOLDED = new DeviceState( + new DeviceState.Configuration.Builder(0 /* identifier */, "FOLDED" /* name */) + .setSystemProperties( + Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY, + PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP)) + .setPhysicalProperties( + Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)) + .build()); + private static final DeviceState FOLD_STATE_UNFOLDED = new DeviceState( + new DeviceState.Configuration.Builder(1 /* identifier */, "UNFOLDED" /* name */) + .setSystemProperties( + Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY, + PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE)) + .setPhysicalProperties( + Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)) + .build()); private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); @@ -260,7 +282,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private QuickSettingsController mQuickSettingsController; @Mock private IStatusBarService mBarService; @Mock private IDreamManager mDreamManager; - @Mock private LightRevealScrimViewModel mLightRevealScrimViewModel; @Mock private LightRevealScrim mLightRevealScrim; @Mock private DozeScrimController mDozeScrimController; @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; @@ -430,6 +451,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0)); when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0)); when(mPowerManagerService.isInteractive()).thenReturn(true); + when(mDeviceStateManager.getSupportedDeviceStates()) + .thenReturn(List.of(FOLD_STATE_FOLDED, FOLD_STATE_UNFOLDED)); doAnswer(invocation -> { OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0]; @@ -513,9 +536,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mLightBarController, mAutoHideController, new StatusBarInitializerImpl( - mContext.getDisplayId(), - mStatusBarWindowControllerStore, + mStatusBarWindowController, mCollapsedStatusBarFragmentProvider, + mock(StatusBarRootFactory.class), + mock(HomeStatusBarComponent.Factory.class), emptySet()), mStatusBarWindowControllerStore, mStatusBarWindowStateController, @@ -605,7 +629,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mWiredChargingRippleController, mDreamManager, mCameraLauncherLazy, - () -> mLightRevealScrimViewModel, mLightRevealScrim, mAlternateBouncerInteractor, mUserTracker, @@ -859,34 +882,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test - @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void testSetDozingNotUnlocking_transitionToAuthScrimmed_cancelKeyguardFadingAway() { - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true); - - mCentralSurfaces.updateScrimController(); - - verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE)); - verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway(); - } - - @Test - @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void testOccludingQSNotExpanded_flagOff_transitionToAuthScrimmed() { - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // GIVEN device occluded and panel is NOT expanded - mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE - when(mKeyguardStateController.isOccluded()).thenReturn(true); - when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false); - - mCentralSurfaces.updateScrimController(); - - verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED)); - } - - @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) public void testNotOccluding_QSNotExpanded_flagOn_doesNotTransitionScrimState() { when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); @@ -902,7 +897,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) public void testNotOccluding_QSExpanded_flagOn_doesTransitionScrimStateToKeyguard() { when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); @@ -918,21 +912,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test - @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void testOccludingQSExpanded_transitionToAuthScrimmedShade() { - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // GIVEN device occluded and qs IS expanded - mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE - when(mKeyguardStateController.isOccluded()).thenReturn(true); - when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true); - - mCentralSurfaces.updateScrimController(); - - verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE)); - } - - @Test public void testEnteringGlanceableHub_updatesScrim() { // Transition to the glanceable hub. mKosmos.getCommunalRepository() @@ -1041,8 +1020,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Test public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() { - setFoldedStates(FOLD_STATE_FOLDED); - setGoToSleepStates(FOLD_STATE_FOLDED); + setFoldedStates(FOLD_STATE_FOLDED.getIdentifier()); + setGoToSleepStates(FOLD_STATE_FOLDED.getIdentifier()); mCentralSurfaces.setBarStateForTest(SHADE); when(mNotificationPanelViewController.isShadeFullyExpanded()).thenReturn(true); @@ -1053,8 +1032,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Test public void deviceStateChange_unfolded_shadeOpen_onKeyguard_doesNotSetLeaveOpenOnKeyguardHide() { - setFoldedStates(FOLD_STATE_FOLDED); - setGoToSleepStates(FOLD_STATE_FOLDED); + setFoldedStates(FOLD_STATE_FOLDED.getIdentifier()); + setGoToSleepStates(FOLD_STATE_FOLDED.getIdentifier()); mCentralSurfaces.setBarStateForTest(KEYGUARD); when(mNotificationPanelViewController.isShadeFullyExpanded()).thenReturn(true); @@ -1066,8 +1045,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Test public void deviceStateChange_unfolded_shadeClose_doesNotSetLeaveOpenOnKeyguardHide() { - setFoldedStates(FOLD_STATE_FOLDED); - setGoToSleepStates(FOLD_STATE_FOLDED); + setFoldedStates(FOLD_STATE_FOLDED.getIdentifier()); + setGoToSleepStates(FOLD_STATE_FOLDED.getIdentifier()); mCentralSurfaces.setBarStateForTest(SHADE); when(mNotificationPanelViewController.isShadeFullyExpanded()).thenReturn(false); @@ -1078,8 +1057,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Test public void deviceStateChange_unfolded_shadeExpanding_onKeyguard_closesQS() { - setFoldedStates(FOLD_STATE_FOLDED); - setGoToSleepStates(FOLD_STATE_FOLDED); + setFoldedStates(FOLD_STATE_FOLDED.getIdentifier()); + setGoToSleepStates(FOLD_STATE_FOLDED.getIdentifier()); mCentralSurfaces.setBarStateForTest(KEYGUARD); when(mNotificationPanelViewController.isExpandingOrCollapsing()).thenReturn(true); @@ -1091,8 +1070,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Test public void deviceStateChange_unfolded_shadeExpanded_onKeyguard_closesQS() { - setFoldedStates(FOLD_STATE_FOLDED); - setGoToSleepStates(FOLD_STATE_FOLDED); + setFoldedStates(FOLD_STATE_FOLDED.getIdentifier()); + setGoToSleepStates(FOLD_STATE_FOLDED.getIdentifier()); mCentralSurfaces.setBarStateForTest(KEYGUARD); when(mNotificationPanelViewController.isShadeFullyExpanded()).thenReturn(true); @@ -1409,9 +1388,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mCentralSurfaces.updateIsKeyguard(false /* forceStateChange */); } - private void setDeviceState(int state) { - DeviceState deviceState = new DeviceState( - new DeviceState.Configuration.Builder(state, "TEST").build()); + private void setDeviceState(DeviceState deviceState) { ArgumentCaptor<DeviceStateManager.DeviceStateCallback> callbackCaptor = ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class); verify(mDeviceStateManager).registerCallback(any(), callbackCaptor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt index a3e2d1949a29..2e65478714af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt @@ -15,12 +15,15 @@ */ package com.android.systemui.statusbar.phone -import android.hardware.devicestate.DeviceState import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase +import com.android.systemui.foldedDeviceStateList +import com.android.systemui.halfFoldedDeviceState +import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.phone.FoldStateListener.OnFoldStateChangeListener +import com.android.systemui.unfoldedDeviceState import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -34,8 +37,7 @@ import org.mockito.MockitoAnnotations.initMocks @SmallTest class FoldStateListenerTest : SysuiTestCase() { - @Mock - private lateinit var listener: OnFoldStateChangeListener + @Mock private lateinit var listener: OnFoldStateChangeListener private lateinit var sut: FoldStateListener @Before @@ -111,25 +113,13 @@ class FoldStateListenerTest : SysuiTestCase() { } private fun setFoldedStates(vararg states: Int) { - mContext.orCreateTestableResources.addOverride( - R.array.config_foldedDeviceStates, - states - ) + mContext.orCreateTestableResources.addOverride(R.array.config_foldedDeviceStates, states) } companion object { - private val DEVICE_STATE_FOLDED = DeviceState( - DeviceState.Configuration.Builder(123 /* id */, "FOLDED" /* name */) - .build() - ) - private val DEVICE_STATE_HALF_FOLDED = DeviceState( - DeviceState.Configuration.Builder(456 /* id */, "HALF_FOLDED" /* name */) - .build() - ) - private val DEVICE_STATE_UNFOLDED = DeviceState( - DeviceState.Configuration.Builder(789 /* id */, "UNFOLDED" /* name */) - .build() - ) + private val DEVICE_STATE_FOLDED = Kosmos().foldedDeviceStateList.first() + private val DEVICE_STATE_HALF_FOLDED = Kosmos().halfFoldedDeviceState + private val DEVICE_STATE_UNFOLDED = Kosmos().unfoldedDeviceState private const val FOLDED = true private const val NOT_FOLDED = false diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index e804b33db1f7..c639b3ab37bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -44,7 +44,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.animation.Animator; -import android.app.AlarmManager; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; @@ -90,7 +89,6 @@ import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.utils.os.FakeHandler; -import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository; import com.google.common.truth.Expect; @@ -139,7 +137,6 @@ public class ScrimControllerTest extends SysuiTestCase { private boolean mAlwaysOnEnabled; private TestableLooper mLooper; private Context mContext; - @Mock private AlarmManager mAlarmManager; @Mock private DozeParameters mDozeParameters; @Mock private LightBarController mLightBarController; @Mock private DelayedWakeLock.Factory mDelayedWakeLockFactory; @@ -157,7 +154,6 @@ public class ScrimControllerTest extends SysuiTestCase { private final FakeKeyguardTransitionRepository mKeyguardTransitionRepository = mKosmos.getKeyguardTransitionRepository(); @Mock private KeyguardInteractor mKeyguardInteractor; - private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository(); @Mock private TypedArray mMockTypedArray; // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The @@ -282,7 +278,6 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController = new ScrimController( mLightBarController, mDozeParameters, - mAlarmManager, mKeyguardStateController, mDelayedWakeLockFactory, new FakeHandler(mLooper.getLooper()), @@ -298,10 +293,8 @@ public class ScrimControllerTest extends SysuiTestCase { mAlternateBouncerToGoneTransitionViewModel, mKeyguardTransitionInteractor, mKeyguardInteractor, - mWallpaperRepository, mKosmos.getTestDispatcher(), mLinearLargeScreenShadeInterpolator); - mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); @@ -309,9 +302,6 @@ public class ScrimControllerTest extends SysuiTestCase { // Attach behind scrim so flows that are collecting on it start running. ViewUtils.attachView(mScrimBehind); - mScrimController.setHasBackdrop(false); - - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); mTestScope.getTestScheduler().runCurrent(); if (SceneContainerFlag.isEnabled()) { @@ -438,8 +428,6 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimInFront, true, mScrimBehind, true )); - - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); } @Test @@ -451,72 +439,6 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimInFront, TRANSPARENT, mScrimBehind, TRANSPARENT, mNotificationsScrim, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); - - assertScrimTinted(Map.of( - mScrimInFront, true, - mScrimBehind, true - )); - } - - @Test - public void transitionToAod_withAodWallpaper() { - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.legacyTransitionTo(ScrimState.AOD); - finishAnimationsImmediately(); - - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, TRANSPARENT)); - assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); - - // Pulsing notification should conserve AOD wallpaper. - mScrimController.legacyTransitionTo(ScrimState.PULSING); - finishAnimationsImmediately(); - - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, TRANSPARENT)); - assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); - } - - @Test - public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() { - mScrimController.setHasBackdrop(true); - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.legacyTransitionTo(ScrimState.AOD); - finishAnimationsImmediately(); - - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); - - assertScrimTinted(Map.of( - mScrimInFront, true, - mScrimBehind, true - )); - } - - @Test - public void setHasBackdrop_withAodWallpaperAndAlbumArt() { - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.legacyTransitionTo(ScrimState.AOD); - finishAnimationsImmediately(); - mScrimController.setHasBackdrop(true); - finishAnimationsImmediately(); - - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); - assertScrimTinted(Map.of( mScrimInFront, true, mScrimBehind, true @@ -540,14 +462,12 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, SEMI_TRANSPARENT, mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // ... and that if we set it while we're in AOD, it does take immediate effect. mScrimController.setAodFrontScrimAlpha(1f); assertScrimAlpha(Map.of( mScrimInFront, OPAQUE, mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // ... and make sure we recall the previous front scrim alpha even if we transition away // for a bit. @@ -557,7 +477,6 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, OPAQUE, mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // ... and alpha updates should be completely ignored if always_on is off. // Passing it forward would mess up the wake-up transition. @@ -588,7 +507,6 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, OPAQUE, mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // ... but will take effect after docked when(mDockManager.isDocked()).thenReturn(true); @@ -600,7 +518,6 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, SEMI_TRANSPARENT, mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // ... and that if we set it while we're in AOD, it does take immediate effect after docked. mScrimController.setAodFrontScrimAlpha(1f); @@ -608,7 +525,6 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, OPAQUE, mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // Reset value since enums are static. mScrimController.setAodFrontScrimAlpha(0f); @@ -619,7 +535,6 @@ public class ScrimControllerTest extends SysuiTestCase { // Pre-condition // Need to go to AoD first because PULSING doesn't change // the back scrim opacity - otherwise it would hide AoD wallpapers. - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); mTestScope.getTestScheduler().runCurrent(); mScrimController.legacyTransitionTo(ScrimState.AOD); @@ -627,7 +542,6 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); mScrimController.legacyTransitionTo(ScrimState.PULSING); finishAnimationsImmediately(); @@ -637,7 +551,6 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); assertScrimTinted(Map.of( mScrimInFront, true, @@ -651,15 +564,12 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, SEMI_TRANSPARENT, mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); mScrimController.setWakeLockScreenSensorActive(true); finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimInFront, SEMI_TRANSPARENT, mScrimBehind, TRANSPARENT)); - assertEquals(ScrimController.WAKE_SENSOR_SCRIM_ALPHA, - mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); // Reset value since enums are static. mScrimController.setAodFrontScrimAlpha(0f); @@ -1333,7 +1243,6 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController = new ScrimController( mLightBarController, mDozeParameters, - mAlarmManager, mKeyguardStateController, mDelayedWakeLockFactory, new FakeHandler(mLooper.getLooper()), @@ -1349,15 +1258,11 @@ public class ScrimControllerTest extends SysuiTestCase { mAlternateBouncerToGoneTransitionViewModel, mKeyguardTransitionInteractor, mKeyguardInteractor, - mWallpaperRepository, mKosmos.getTestDispatcher(), mLinearLargeScreenShadeInterpolator); - mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); - mScrimController.setHasBackdrop(false); - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); mTestScope.getTestScheduler().runCurrent(); mScrimController.legacyTransitionTo(ScrimState.KEYGUARD); finishAnimationsImmediately(); @@ -1454,57 +1359,6 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void testHoldsAodWallpaperAnimationLock() { - // Pre-conditions - mScrimController.legacyTransitionTo(ScrimState.AOD); - finishAnimationsImmediately(); - reset(mWakeLock); - - mScrimController.onHideWallpaperTimeout(); - verify(mWakeLock).acquire(anyString()); - verify(mWakeLock, never()).release(anyString()); - finishAnimationsImmediately(); - verify(mWakeLock).release(anyString()); - } - - @Test - public void testHoldsPulsingWallpaperAnimationLock() { - // Pre-conditions - mScrimController.legacyTransitionTo(ScrimState.PULSING); - finishAnimationsImmediately(); - reset(mWakeLock); - - mScrimController.onHideWallpaperTimeout(); - verify(mWakeLock).acquire(anyString()); - verify(mWakeLock, never()).release(anyString()); - finishAnimationsImmediately(); - verify(mWakeLock).release(anyString()); - } - - @Test - public void testWillHideAodWallpaper() { - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.legacyTransitionTo(ScrimState.AOD); - verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any()); - mScrimController.legacyTransitionTo(ScrimState.KEYGUARD); - verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class)); - } - - @Test - public void testWillHideDockedWallpaper() { - mAlwaysOnEnabled = false; - when(mDockManager.isDocked()).thenReturn(true); - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.legacyTransitionTo(ScrimState.AOD); - - verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any()); - } - - @Test public void testConservesExpansionOpacityAfterTransition() { mScrimController.legacyTransitionTo(ScrimState.UNLOCKED); mScrimController.setRawPanelExpansionFraction(0.5f); @@ -1542,43 +1396,6 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void testHidesShowWhenLockedActivity() { - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.setKeyguardOccluded(true); - mScrimController.legacyTransitionTo(ScrimState.AOD); - finishAnimationsImmediately(); - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, OPAQUE)); - - mScrimController.legacyTransitionTo(ScrimState.PULSING); - finishAnimationsImmediately(); - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, OPAQUE)); - } - - @Test - public void testHidesShowWhenLockedActivity_whenAlreadyInAod() { - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.legacyTransitionTo(ScrimState.AOD); - finishAnimationsImmediately(); - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, TRANSPARENT)); - - mScrimController.setKeyguardOccluded(true); - finishAnimationsImmediately(); - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, OPAQUE)); - } - - @Test public void testEatsTouchEvent() { HashSet<ScrimState> eatsTouches = new HashSet<>(Collections.singletonList(ScrimState.AOD)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index e57e8d108529..d01c1ca36c4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone.fragment; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS; import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS; import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED; @@ -61,17 +60,18 @@ import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager; -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent; +import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ui.DarkIconManager; import com.android.systemui.statusbar.phone.ui.StatusBarIconController; -import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewBinder; -import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel; +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewBinder; +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewModel; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; @@ -109,9 +109,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private final CarrierConfigTracker mCarrierConfigTracker = mock(CarrierConfigTracker.class); @Mock - private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory; + private HomeStatusBarComponent.Factory mStatusBarFragmentComponentFactory; @Mock - private StatusBarFragmentComponent mStatusBarFragmentComponent; + private HomeStatusBarComponent mHomeStatusBarComponent; @Mock private StatusBarStateController mStatusBarStateController; @Mock @@ -122,8 +122,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private DarkIconManager.Factory mIconManagerFactory; @Mock private DarkIconManager mIconManager; - private FakeCollapsedStatusBarViewModel mCollapsedStatusBarViewModel; - private FakeCollapsedStatusBarViewBinder mCollapsedStatusBarViewBinder; + private FakeHomeStatusBarViewModel mCollapsedStatusBarViewModel; + private FakeHomeStatusBarViewBinder mCollapsedStatusBarViewBinder; @Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; @Mock @@ -633,7 +633,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, - FLAG_STATUS_BAR_RON_CHIPS, + StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() { resumeAndGetFragment(); @@ -660,8 +660,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasSecondaryOngoingActivity_butRonsFlagOff_secondaryChipHidden() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() { resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -673,7 +673,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() { resumeAndGetFragment(); @@ -689,8 +689,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -705,9 +705,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_ronsFlagOn() { + public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -724,8 +724,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivityButAlsoHun_chipHidden_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -740,9 +740,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivitiesButAlsoHun_chipsHidden_ronsFlagOn() { + public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -759,8 +759,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void primaryOngoingActivityEnded_chipHidden_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() { resumeAndGetFragment(); // Ongoing activity started @@ -781,9 +781,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void primaryOngoingActivityEnded_chipHidden_ronsFlagOn() { + public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() { resumeAndGetFragment(); // Ongoing activity started @@ -804,7 +804,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) public void secondaryOngoingActivityEnded_chipHidden() { resumeAndGetFragment(); @@ -828,8 +828,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating fragment.enableAnimationsForTesting(); @@ -846,9 +846,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOn() { + public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating fragment.enableAnimationsForTesting(); @@ -866,8 +866,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // WHEN there *is* an ongoing call via old callback @@ -898,9 +898,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOn() { + public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // WHEN there *is* an ongoing call via old callback @@ -1060,7 +1060,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { public void setUp_fragmentCreatesDaggerComponent() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); - assertEquals(mStatusBarFragmentComponent, fragment.getStatusBarFragmentComponent()); + assertEquals(mHomeStatusBarComponent, fragment.getHomeStatusBarComponent()); } @Test @@ -1190,8 +1190,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mSecureSettings = mock(SecureSettings.class); mShadeExpansionStateManager = new ShadeExpansionStateManager(); - mCollapsedStatusBarViewModel = new FakeCollapsedStatusBarViewModel(); - mCollapsedStatusBarViewBinder = new FakeCollapsedStatusBarViewBinder(); + mCollapsedStatusBarViewModel = new FakeHomeStatusBarViewModel(); + mCollapsedStatusBarViewBinder = new FakeHomeStatusBarViewBinder(); return new CollapsedStatusBarFragment( mStatusBarFragmentComponentFactory, @@ -1224,8 +1224,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private void setUpDaggerComponent() { when(mStatusBarFragmentComponentFactory.create(any())) - .thenReturn(mStatusBarFragmentComponent); - when(mStatusBarFragmentComponent.getHeadsUpAppearanceController()) + .thenReturn(mHomeStatusBarComponent); + when(mHomeStatusBarComponent.getHeadsUpAppearanceController()) .thenReturn(mHeadsUpAppearanceController); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java index f6e07d3d621e..3247a1ab6eb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java @@ -16,6 +16,11 @@ package com.android.systemui.statusbar.policy; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN; import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED; import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED; import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED; @@ -24,7 +29,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; import android.os.UserHandle; @@ -51,14 +58,37 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Collections; +import java.util.List; +import java.util.Set; + @RunWith(AndroidJUnit4.class) @SmallTest public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase { + private static final DeviceState DEFAULT_FOLDED_STATE = createDeviceState(0 /* identifier */, + "folded", Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY), + Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)); + private static final DeviceState DEFAULT_HALF_FOLDED_STATE = createDeviceState( + 2 /* identifier */, "half_folded", + Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY), + Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN)); + private static final DeviceState DEFAULT_UNFOLDED_STATE = createDeviceState(1 /* identifier */, + "unfolded", + Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY), + Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)); + private static final DeviceState UNKNOWN_DEVICE_STATE = createDeviceState(8 /* identifier */, + "unknown", Collections.emptySet(), Collections.emptySet()); + private static final List<DeviceState> DEVICE_STATE_LIST = List.of(DEFAULT_FOLDED_STATE, + DEFAULT_HALF_FOLDED_STATE, DEFAULT_UNFOLDED_STATE); + private static final String[] DEFAULT_SETTINGS = new String[]{"0:1", "2:0:1", "1:2"}; - private static final int[] DEFAULT_FOLDED_STATES = new int[]{0}; - private static final int[] DEFAULT_HALF_FOLDED_STATES = new int[]{2}; - private static final int[] DEFAULT_UNFOLDED_STATES = new int[]{1}; + private static final int[] DEFAULT_FOLDED_STATE_IDENTIFIERS = + new int[]{DEFAULT_FOLDED_STATE.getIdentifier()}; + private static final int[] DEFAULT_HALF_FOLDED_STATE_IDENTIFIERS = + new int[]{DEFAULT_HALF_FOLDED_STATE.getIdentifier()}; + private static final int[] DEFAULT_UNFOLDED_STATE_IDENTIFIERS = + new int[]{DEFAULT_UNFOLDED_STATE.getIdentifier()}; @Mock private DeviceStateManager mDeviceStateManager; @Mock private DeviceStateRotationLockSettingControllerLogger mLogger; @@ -77,10 +107,12 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase MockitoAnnotations.initMocks(/* testClass= */ this); TestableResources resources = mContext.getOrCreateTestableResources(); resources.addOverride(R.array.config_perDeviceStateRotationLockDefaults, DEFAULT_SETTINGS); - resources.addOverride(R.array.config_foldedDeviceStates, DEFAULT_FOLDED_STATES); - resources.addOverride(R.array.config_halfFoldedDeviceStates, DEFAULT_HALF_FOLDED_STATES); - resources.addOverride(R.array.config_openDeviceStates, DEFAULT_UNFOLDED_STATES); - + resources.addOverride(R.array.config_foldedDeviceStates, DEFAULT_FOLDED_STATE_IDENTIFIERS); + resources.addOverride(R.array.config_halfFoldedDeviceStates, + DEFAULT_HALF_FOLDED_STATE_IDENTIFIERS); + resources.addOverride(R.array.config_openDeviceStates, DEFAULT_UNFOLDED_STATE_IDENTIFIERS); + when(mDeviceStateManager.getSupportedDeviceStates()).thenReturn(DEVICE_STATE_LIST); + mContext.addMockSystemService(DeviceStateManager.class, mDeviceStateManager); ArgumentCaptor<DeviceStateManager.DeviceStateCallback> deviceStateCallbackArgumentCaptor = ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class); @@ -120,11 +152,11 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED); mFakeRotationPolicy.setRotationLock(true); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_UNFOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); // Settings only exist for state 0 and 1 - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_HALF_FOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); } @@ -135,10 +167,10 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_LOCKED); mFakeRotationPolicy.setRotationLock(true); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_FOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_UNFOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue(); } @@ -148,7 +180,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase mFakeRotationPolicy.setRotationLock(true); // State 2 -> Ignored -> Fall back to state 1 which is unlocked - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_HALF_FOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); } @@ -162,7 +194,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase mFakeRotationPolicy.setRotationLock(false); // State 2 -> Ignored -> Fall back to state 1 which is locked - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_HALF_FOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue(); } @@ -174,7 +206,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase mSettingsManager.onPersistedSettingsChanged(); mFakeRotationPolicy.setRotationLock(true); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_FOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue(); mDeviceStateRotationLockSettingController.onRotationLockStateChanged( @@ -190,10 +222,10 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase @Test public void whenDeviceStateSwitchedToIgnoredState_useFallbackSetting() { - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_FOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue(); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_UNFOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); } @@ -203,10 +235,10 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase 8, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED); mFakeRotationPolicy.setRotationLock(true); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_UNFOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(8)); + mDeviceStateCallback.onDeviceStateChanged(UNKNOWN_DEVICE_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); mDeviceStateRotationLockSettingController.onRotationLockStateChanged( @@ -226,7 +258,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED); mFakeRotationPolicy.setRotationLock(false); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_FOLDED_STATE); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); @@ -242,7 +274,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase initializeSettingsWith( 0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_FOLDED_STATE); mDeviceStateRotationLockSettingController.onRotationLockStateChanged( /* rotationLocked= */ false, @@ -263,7 +295,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase 0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 2, DEVICE_STATE_ROTATION_LOCK_IGNORED); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_HALF_FOLDED_STATE); mDeviceStateRotationLockSettingController.onRotationLockStateChanged( /* rotationLocked= */ true, @@ -284,8 +316,8 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase 0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 8, DEVICE_STATE_ROTATION_LOCK_IGNORED); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1)); - mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(8)); + mDeviceStateCallback.onDeviceStateChanged(DEFAULT_UNFOLDED_STATE); + mDeviceStateCallback.onDeviceStateChanged(UNKNOWN_DEVICE_STATE); mDeviceStateRotationLockSettingController.onRotationLockStateChanged( /* rotationLocked= */ true, @@ -321,8 +353,13 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase mSettingsManager.onPersistedSettingsChanged(); } - private DeviceState createDeviceStateForIdentifier(int id) { - return new DeviceState(new DeviceState.Configuration.Builder(id, "" /* name */).build()); + private static DeviceState createDeviceState(int identifier, @NonNull String name, + @NonNull Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties, + @NonNull Set<@DeviceState.PhysicalDeviceStateProperties Integer> physicalProperties) { + DeviceState.Configuration deviceStateConfiguration = new DeviceState.Configuration.Builder( + identifier, name).setSystemProperties(systemProperties).setPhysicalProperties( + physicalProperties).build(); + return new DeviceState(deviceStateConfiguration); } private static class FakeRotationPolicy implements RotationPolicyWrapper { diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 5996ef13a463..1ceb20adeebd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -198,6 +198,8 @@ import com.android.wm.shell.taskview.TaskView; import com.android.wm.shell.taskview.TaskViewTransitions; import com.android.wm.shell.transition.Transitions; +import kotlin.Lazy; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -216,7 +218,6 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.Executor; -import kotlin.Lazy; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; @@ -2481,7 +2482,7 @@ public class BubblesTest extends SysuiTestCase { mEntryListener.onEntryAdded(mRow); - verify(mBubbleLogger).log(argThat(b -> b.getKey().equals(mRow.getKey())), + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED)); } @@ -2498,10 +2499,25 @@ public class BubblesTest extends SysuiTestCase { NotificationEntryHelper.modifyRanking(mRow).setTextChanged(true).build(); mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true); - verify(mBubbleLogger).log(argThat(b -> b.getKey().equals(mRow.getKey())), + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_UPDATED)); } + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_dragBubbleToDismiss() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + mBubbleController.dragBubbleToDismiss(mRow.getKey(), 1L); + + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE)); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); @@ -2687,6 +2703,10 @@ public class BubblesTest extends SysuiTestCase { assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded); } + private Bubble eqBubbleWithKey(String key) { + return argThat(b -> b.getKey().equals(key)); + } + private static class FakeBubbleStateListener implements Bubbles.BubbleStateListener { int mStateChangeCalls = 0; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/DeviceStateManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/DeviceStateManagerKosmos.kt new file mode 100644 index 000000000000..9c55820b797c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/DeviceStateManagerKosmos.kt @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui + +import android.hardware.devicestate.DeviceState +import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN +import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN +import android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP +import android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE +import android.hardware.devicestate.DeviceStateManager +import com.android.systemui.kosmos.Kosmos +import org.mockito.kotlin.mock + +val Kosmos.deviceStateManager by Kosmos.Fixture { mock<DeviceStateManager>() } + +val Kosmos.defaultDeviceState by + Kosmos.Fixture { + DeviceState(DeviceState.Configuration.Builder(0 /* identifier */, "DEFAULT").build()) + } + +val Kosmos.foldedDeviceStateList by + Kosmos.Fixture { + listOf( + DeviceState( + DeviceState.Configuration.Builder(0, "FOLDED_0") + .setSystemProperties( + setOf( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY, + PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP + ) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED) + ) + .build() + ), + DeviceState( + DeviceState.Configuration.Builder(1, "FOLDED_1") + .setSystemProperties( + setOf( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY, + PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP + ) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED) + ) + .build() + ), + DeviceState( + DeviceState.Configuration.Builder(2, "FOLDED_2") + .setSystemProperties( + setOf( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY, + PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP + ) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED) + ) + .build() + ) + ) + } + +val Kosmos.halfFoldedDeviceState by + Kosmos.Fixture { + DeviceState( + DeviceState.Configuration.Builder(3 /* identifier */, "HALF_FOLDED") + .setSystemProperties( + setOf( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY, + PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE + ) + ) + .setPhysicalProperties( + setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN) + ) + .build() + ) + } + +val Kosmos.unfoldedDeviceState by + Kosmos.Fixture { + DeviceState( + DeviceState.Configuration.Builder(4 /* identifier */, "UNFOLDED") + .setSystemProperties( + setOf( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY, + PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE + ) + ) + .setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)) + .build() + ) + } + +val Kosmos.rearDisplayDeviceState by + Kosmos.Fixture { + DeviceState( + DeviceState.Configuration.Builder(5 /* identifier */, "REAR_DISPLAY") + .setSystemProperties( + setOf( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY, + PROPERTY_FEATURE_REAR_DISPLAY + ) + ) + .build() + ) + } + +val Kosmos.unknownDeviceState by + Kosmos.Fixture { + DeviceState(DeviceState.Configuration.Builder(8 /* identifier */, "UNKNOWN").build()) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt index baaf60447cf9..703e2d1db200 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt @@ -49,8 +49,6 @@ class FakeKeyguardBouncerRepository @Inject constructor() : KeyguardBouncerRepos private val _isAlternateBouncerVisible = MutableStateFlow(false) override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow() override var lastAlternateBouncerVisibleTime: Long = 0L - private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false) - override val alternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow() override val lastShownSecurityMode: MutableStateFlow<KeyguardSecurityModel.SecurityMode> = MutableStateFlow(KeyguardSecurityModel.SecurityMode.Invalid) override var bouncerDismissActionModel: BouncerDismissActionModel? = null @@ -63,10 +61,6 @@ class FakeKeyguardBouncerRepository @Inject constructor() : KeyguardBouncerRepos _isAlternateBouncerVisible.value = isVisible } - override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) { - _isAlternateBouncerUIAvailable.value = isAvailable - } - override fun setPrimaryShow(isShowing: Boolean) { _primaryBouncerShow.value = isShowing } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt index 63323b239a78..8bbb8a0d320e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt @@ -16,33 +16,23 @@ package com.android.systemui.bouncer.domain.interactor -import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor -import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope -import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.statusbar.policy.keyguardStateController -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.systemClock val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by Kosmos.Fixture { AlternateBouncerInteractor( - statusBarStateController = statusBarStateController, - keyguardStateController = keyguardStateController, bouncerRepository = keyguardBouncerRepository, fingerprintPropertyRepository = fingerprintPropertyRepository, - biometricSettingsRepository = biometricSettingsRepository, systemClock = systemClock, - keyguardUpdateMonitor = keyguardUpdateMonitor, deviceEntryBiometricsAllowedInteractor = { deviceEntryBiometricsAllowedInteractor }, keyguardInteractor = { keyguardInteractor }, keyguardTransitionInteractor = { keyguardTransitionInteractor }, @@ -54,21 +44,9 @@ val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by fun Kosmos.givenCanShowAlternateBouncer() { this.givenAlternateBouncerSupported() this.keyguardBouncerRepository.setPrimaryShow(false) - this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) - this.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) this.deviceEntryFaceAuthRepository.setLockedOut(false) - whenever(this.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false) - whenever(this.keyguardStateController.isUnlocked).thenReturn(false) } fun Kosmos.givenAlternateBouncerSupported() { - if (DeviceEntryUdfpsRefactor.isEnabled) { - this.fingerprintPropertyRepository.supportsUdfps() - } else { - this.keyguardBouncerRepository.setAlternateBouncerUIAvailable(true) - } -} - -fun Kosmos.givenCannotShowAlternateBouncer() { - this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) + this.fingerprintPropertyRepository.supportsUdfps() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt index 3087d01a2479..77ec83871016 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt @@ -23,6 +23,7 @@ import com.android.internal.logging.metricsLogger import com.android.internal.util.emergencyAffordanceManager import com.android.systemui.authentication.domain.interactor.authenticationInteractor import com.android.systemui.bouncer.data.repository.emergencyServicesRepository +import com.android.systemui.haptics.msdl.bouncerHapticPlayer import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.testDispatcher @@ -52,5 +53,6 @@ val Kosmos.bouncerActionButtonInteractor by Fixture { metricsLogger = metricsLogger, dozeLogger = mock(), sceneInteractor = { sceneInteractor }, + bouncerHapticPlayer, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt new file mode 100644 index 000000000000..e3797260ed6d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.display.data.repository + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import kotlinx.coroutines.CoroutineScope + +class FakePerDisplayStore( + backgroundApplicationScope: CoroutineScope, + displayRepository: DisplayRepository, +) : PerDisplayStoreImpl<TestPerDisplayInstance>(backgroundApplicationScope, displayRepository) { + + val removalActions = mutableListOf<TestPerDisplayInstance>() + + override fun createInstanceForDisplay(displayId: Int): TestPerDisplayInstance { + return TestPerDisplayInstance(displayId) + } + + override val instanceClass = TestPerDisplayInstance::class.java + + override suspend fun onDisplayRemovalAction(instance: TestPerDisplayInstance) { + removalActions += instance + } +} + +data class TestPerDisplayInstance(val displayId: Int) + +val Kosmos.fakePerDisplayStore by + Kosmos.Fixture { + FakePerDisplayStore( + backgroundApplicationScope = applicationCoroutineScope, + displayRepository = displayRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt index c0152b26d7a3..41402badf141 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt @@ -17,7 +17,6 @@ package com.android.systemui.flags import android.platform.test.annotations.EnableFlags -import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT @@ -36,7 +35,6 @@ import com.android.systemui.Flags.FLAG_SCENE_CONTAINER FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN, FLAG_PREDICTIVE_BACK_SYSUI, FLAG_SCENE_CONTAINER, - FLAG_DEVICE_ENTRY_UDFPS_REFACTOR, ) @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/fragments/FragmentServiceKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/fragments/FragmentServiceKosmos.kt new file mode 100644 index 000000000000..c088685515fd --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/fragments/FragmentServiceKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.fragments + +import com.android.systemui.kosmos.Kosmos +import org.mockito.kotlin.mock + +val Kosmos.mockFragmentService by Kosmos.Fixture { mock<FragmentService>() } + +var Kosmos.fragmentService by Kosmos.Fixture { mockFragmentService } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt index 805a710a2cf3..4181dc302049 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt @@ -40,6 +40,8 @@ class FakeLightRevealScrimRepository : LightRevealScrimRepository { override val isAnimating: Boolean get() = false + override val maxAlpha: MutableStateFlow<Float> = MutableStateFlow(1f) + override fun startRevealAmountAnimator(reveal: Boolean, duration: Long) { if (reveal) { _revealAmount.value = 1.0f @@ -50,8 +52,5 @@ class FakeLightRevealScrimRepository : LightRevealScrimRepository { revealAnimatorRequests.add(RevealAnimatorRequest(reveal, duration)) } - data class RevealAnimatorRequest( - val reveal: Boolean, - val duration: Long - ) + data class RevealAnimatorRequest(val reveal: Boolean, val duration: Long) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt index 4ccee6f52fa2..2aa27444a058 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt @@ -22,18 +22,16 @@ import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor val Kosmos.deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor by Kosmos.Fixture { DeviceEntrySideFpsOverlayInteractor( - applicationScope = testScope.backgroundScope, context = applicationContext, deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository, sceneInteractor = sceneInteractor, primaryBouncerInteractor = primaryBouncerInteractor, alternateBouncerInteractor = alternateBouncerInteractor, - keyguardUpdateMonitor = keyguardUpdateMonitor + keyguardUpdateMonitor = keyguardUpdateMonitor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt deleted file mode 100644 index 7ebef10b12c6..000000000000 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.keyguard.domain.interactor - -import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository -import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.applicationCoroutineScope -import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.power.domain.interactor.powerInteractor -import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor - -var Kosmos.fromDreamingLockscreenHostedTransitionInteractor by - Kosmos.Fixture { - FromDreamingLockscreenHostedTransitionInteractor( - transitionRepository = keyguardTransitionRepository, - transitionInteractor = keyguardTransitionInteractor, - internalTransitionInteractor = internalKeyguardTransitionInteractor, - scope = applicationCoroutineScope, - bgDispatcher = testDispatcher, - mainDispatcher = testDispatcher, - keyguardInteractor = keyguardInteractor, - powerInteractor = powerInteractor, - keyguardOcclusionInteractor = keyguardOcclusionInteractor, - ) - } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt index 0ca025f53df4..742b79cc17d0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt @@ -19,9 +19,6 @@ package com.android.systemui.qs.panels.data.repository import android.content.res.mainResources import com.android.systemui.common.ui.data.repository.configurationRepository import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.applicationCoroutineScope -val Kosmos.qsColumnsRepository by - Kosmos.Fixture { - QSColumnsRepository(applicationCoroutineScope, mainResources, configurationRepository) - } +var Kosmos.qsColumnsRepository by + Kosmos.Fixture { QSColumnsRepository(mainResources, configurationRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt index 02ed2648107b..47615f527d16 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt @@ -17,6 +17,11 @@ package com.android.systemui.qs.panels.domain.interactor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.qs.panels.data.repository.qsColumnsRepository +import com.android.systemui.shade.domain.interactor.shadeInteractor -val Kosmos.qsColumnsInteractor by Kosmos.Fixture { QSColumnsInteractor(qsColumnsRepository) } +val Kosmos.qsColumnsInteractor by + Kosmos.Fixture { + QSColumnsInteractor(applicationCoroutineScope, qsColumnsRepository, shadeInteractor) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt new file mode 100644 index 000000000000..e16756befb03 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.tiles.impl.hearingdevices + +import com.android.systemui.accessibility.qs.QSAccessibilityModule +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.qsEventLogger + +val Kosmos.qsHearingDevicesTileConfig by + Kosmos.Fixture { QSAccessibilityModule.provideHearingDevicesTileConfig(qsEventLogger) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt index c0d65a076ca0..2316a2fdcd2b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel import android.content.packageManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.util.time.fakeSystemClock -val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by +val Kosmos.demoNotifChipViewModel: DemoNotifChipViewModel by Kosmos.Fixture { - DemoRonChipViewModel( + DemoNotifChipViewModel( commandRegistry = commandRegistry, packageManager = packageManager, systemClock = fakeSystemClock, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt new file mode 100644 index 000000000000..af24c371d62b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.ui.viewmodel + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor + +val Kosmos.notifChipsViewModel: NotifChipsViewModel by + Kosmos.Fixture { NotifChipsViewModel(activeNotificationsInteractor) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt index 5382c1c4b8d0..0300bf4636ea 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt @@ -20,7 +20,8 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.ui.viewmodel.notifChipsViewModel import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel import com.android.systemui.statusbar.chips.statusBarChipsLogger @@ -33,7 +34,8 @@ val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by shareToAppChipViewModel = shareToAppChipViewModel, castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel, callChipViewModel = callChipViewModel, - demoRonChipViewModel = demoRonChipViewModel, + notifChipsViewModel = notifChipsViewModel, + demoNotifChipViewModel = demoNotifChipViewModel, logger = statusBarChipsLogger, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt index edd660490e4d..9fa3abfdc95c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt @@ -19,11 +19,18 @@ package com.android.systemui.statusbar.core import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions import com.android.systemui.statusbar.phone.PhoneStatusBarViewController +import org.mockito.kotlin.mock -class FakeStatusBarInitializer( - private val statusBarViewController: PhoneStatusBarViewController, - private val statusBarTransitions: PhoneStatusBarTransitions, -) : StatusBarInitializer { +class FakeStatusBarInitializer : StatusBarInitializer { + + val statusBarViewController = mock<PhoneStatusBarViewController>() + val statusBarTransitions = mock<PhoneStatusBarTransitions>() + + var startedByCoreStartable: Boolean = false + private set + + var initializedByCentralSurfaces: Boolean = false + private set override var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null set(value) { @@ -31,5 +38,11 @@ class FakeStatusBarInitializer( value?.onStatusBarViewUpdated(statusBarViewController, statusBarTransitions) } - override fun initializeStatusBar() {} + override fun initializeStatusBar() { + initializedByCentralSurfaces = true + } + + override fun start() { + startedByCoreStartable = true + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt index 73ed228f5aaa..8c218be6c982 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt @@ -16,14 +16,11 @@ package com.android.systemui.statusbar.core -import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions -import com.android.systemui.statusbar.phone.PhoneStatusBarViewController +import com.android.systemui.statusbar.window.StatusBarWindowController -class FakeStatusBarInitializerFactory( - private val statusBarViewController: PhoneStatusBarViewController, - private val statusBarTransitions: PhoneStatusBarTransitions, -) : StatusBarInitializer.Factory { +class FakeStatusBarInitializerFactory() : StatusBarInitializer.Factory { - override fun create(displayId: Int): StatusBarInitializer = - FakeStatusBarInitializer(statusBarViewController, statusBarTransitions) + override fun create( + statusBarWindowController: StatusBarWindowController + ): StatusBarInitializer = FakeStatusBarInitializer() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt new file mode 100644 index 000000000000..0c2cba981824 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.core + +import android.view.Display + +class FakeStatusBarInitializerStore : StatusBarInitializerStore { + + private val initializers = mutableMapOf<Int, FakeStatusBarInitializer>() + + override val defaultDisplay: FakeStatusBarInitializer + get() = forDisplay(Display.DEFAULT_DISPLAY) + + override fun forDisplay(displayId: Int): FakeStatusBarInitializer { + return initializers.computeIfAbsent(displayId) { FakeStatusBarInitializer() } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt new file mode 100644 index 000000000000..9197dcdc3f68 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.core + +import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository +import kotlinx.coroutines.CoroutineScope +import org.mockito.kotlin.mock + +class FakeStatusBarOrchestratorFactory : StatusBarOrchestrator.Factory { + + private val createdOrchestrators = mutableMapOf<Int, StatusBarOrchestrator>() + + fun createdOrchestratorForDisplay(displayId: Int): StatusBarOrchestrator? = + createdOrchestrators[displayId] + + override fun create( + displayId: Int, + displayScope: CoroutineScope, + statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository, + statusBarModeRepository: StatusBarModePerDisplayRepository, + statusBarInitializer: StatusBarInitializer, + statusBarWindowController: StatusBarWindowController, + ): StatusBarOrchestrator = + mock<StatusBarOrchestrator>().also { createdOrchestrators[displayId] = it } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt index 7ad715ba5a89..303529b7f7b0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt @@ -19,20 +19,13 @@ package com.android.systemui.statusbar.core import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope -import com.android.systemui.statusbar.phone.phoneStatusBarTransitions -import com.android.systemui.statusbar.phone.phoneStatusBarViewController +import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore -val Kosmos.fakeStatusBarInitializer by - Kosmos.Fixture { - FakeStatusBarInitializer(phoneStatusBarViewController, phoneStatusBarTransitions) - } +val Kosmos.fakeStatusBarInitializer by Kosmos.Fixture { FakeStatusBarInitializer() } var Kosmos.statusBarInitializer by Kosmos.Fixture { fakeStatusBarInitializer } -val Kosmos.fakeStatusBarInitializerFactory by - Kosmos.Fixture { - FakeStatusBarInitializerFactory(phoneStatusBarViewController, phoneStatusBarTransitions) - } +val Kosmos.fakeStatusBarInitializerFactory by Kosmos.Fixture { FakeStatusBarInitializerFactory() } var Kosmos.statusBarInitializerFactory: StatusBarInitializer.Factory by Kosmos.Fixture { fakeStatusBarInitializerFactory } @@ -41,7 +34,13 @@ val Kosmos.multiDisplayStatusBarInitializerStore by Kosmos.Fixture { MultiDisplayStatusBarInitializerStore( applicationCoroutineScope, - fakeStatusBarInitializerFactory, displayRepository, + fakeStatusBarInitializerFactory, + fakeStatusBarWindowControllerStore, ) } + +val Kosmos.fakeStatusBarInitializerStore by Kosmos.Fixture { FakeStatusBarInitializerStore() } + +var Kosmos.statusBarInitializerStore: StatusBarInitializerStore by + Kosmos.Fixture { fakeStatusBarInitializerStore } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt index 54de293b8911..87f7142b8817 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt @@ -16,7 +16,11 @@ package com.android.systemui.statusbar.core +import android.content.testableContext import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.display.data.repository.displayScopeRepository +import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.mockDemoModeController @@ -24,20 +28,25 @@ import com.android.systemui.plugins.mockPluginDependencyProvider import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.shade.mockNotificationShadeWindowViewController import com.android.systemui.shade.mockShadeSurface -import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository +import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository +import com.android.systemui.statusbar.data.repository.statusBarModeRepository import com.android.systemui.statusbar.mockNotificationRemoteInputManager import com.android.systemui.statusbar.phone.mockAutoHideController +import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore -import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore +import com.android.systemui.statusbar.window.fakeStatusBarWindowController +import com.android.systemui.statusbar.window.statusBarWindowControllerStore import com.android.wm.shell.bubbles.bubblesOptional val Kosmos.statusBarOrchestrator by Kosmos.Fixture { StatusBarOrchestrator( + testableContext.displayId, applicationCoroutineScope, + fakeStatusBarWindowStatePerDisplayRepository, + fakeStatusBarModePerDisplayRepository, fakeStatusBarInitializer, - fakeStatusBarModeRepository, - fakeStatusBarWindowControllerStore, + fakeStatusBarWindowController, mockDemoModeController, mockPluginDependencyProvider, mockAutoHideController, @@ -45,8 +54,28 @@ val Kosmos.statusBarOrchestrator by { mockNotificationShadeWindowViewController }, mockShadeSurface, bubblesOptional, - statusBarWindowStateRepositoryStore, + dumpManager, powerInteractor, primaryBouncerInteractor, ) } + +val Kosmos.fakeStatusBarOrchestratorFactory by Kosmos.Fixture { FakeStatusBarOrchestratorFactory() } + +var Kosmos.statusBarOrchestratorFactory: StatusBarOrchestrator.Factory by + Kosmos.Fixture { fakeStatusBarOrchestratorFactory } + +val Kosmos.multiDisplayStatusBarStarter by + Kosmos.Fixture { + MultiDisplayStatusBarStarter( + applicationCoroutineScope, + displayScopeRepository, + statusBarOrchestratorFactory, + statusBarWindowStateRepositoryStore, + statusBarModeRepository, + displayRepository, + statusBarInitializerStore, + statusBarWindowControllerStore, + statusBarInitializerStore, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt index 60690838fcdf..285cebb96cae 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt @@ -20,7 +20,6 @@ import android.view.Display import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.data.model.StatusBarAppearance import com.android.systemui.statusbar.data.model.StatusBarMode -import com.google.common.truth.Truth.assertThat import dagger.Binds import dagger.Module import javax.inject.Inject @@ -37,7 +36,6 @@ class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepositor FakeStatusBarModePerDisplayRepository() override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository { - assertThat(displayId).isEqualTo(DISPLAY_ID) return defaultDisplay } } @@ -51,6 +49,7 @@ class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository override fun showTransient() { isTransientShown.value = true } + override fun clearTransient() { isTransientShown.value = false } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt index 0f2b477db67a..12db2f74197d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt @@ -18,6 +18,9 @@ package com.android.systemui.statusbar.data.repository import com.android.systemui.kosmos.Kosmos +val Kosmos.fakeStatusBarModePerDisplayRepository by + Kosmos.Fixture { FakeStatusBarModePerDisplayRepository() } + val Kosmos.statusBarModeRepository: StatusBarModeRepositoryStore by Kosmos.Fixture { fakeStatusBarModeRepository } val Kosmos.fakeStatusBarModeRepository by Kosmos.Fixture { FakeStatusBarModeRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt index fc4f05df26ed..7f4c670b05aa 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt @@ -69,6 +69,9 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag +import com.android.systemui.statusbar.notification.row.icon.AppIconProviderImpl +import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderImpl +import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger import com.android.systemui.statusbar.phone.KeyguardBypassController @@ -99,7 +102,7 @@ import org.mockito.Mockito class ExpandableNotificationRowBuilder( private val context: Context, dependency: TestableDependency, - private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic() + private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(), ) { private val mMockLogger: ExpandableNotificationRowLogger @@ -161,21 +164,21 @@ class ExpandableNotificationRowBuilder( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "true", - true + true, ) setProperty( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.SSIN_ENABLED, "true", - true + true, ) setProperty( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, "false", - true + true, ) - } + }, ) val remoteViewsFactories = getNotifRemoteViewsFactoryContainer(featureFlags) val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java, STUB_ONLY) @@ -192,21 +195,21 @@ class ExpandableNotificationRowBuilder( Mockito.mock(KeyguardDismissUtil::class.java, STUB_ONLY), remoteInputManager = remoteInputManager, smartReplyController = mSmartReplyController, - context = context + context = context, ), smartActionsInflater = SmartActionInflaterImpl( constants = mSmartReplyConstants, activityStarter = Mockito.mock(ActivityStarter::class.java, STUB_ONLY), smartReplyController = mSmartReplyController, - headsUpManager = mHeadsUpManager - ) + headsUpManager = mHeadsUpManager, + ), ) val notifLayoutInflaterFactoryProvider = object : NotifLayoutInflaterFactory.Provider { override fun provide( row: ExpandableNotificationRow, - layoutType: Int + layoutType: Int, ): NotifLayoutInflaterFactory = NotifLayoutInflaterFactory(row, layoutType, remoteViewsFactories) } @@ -270,14 +273,14 @@ class ExpandableNotificationRowBuilder( whenever( mOnUserInteractionCallback.registerFutureDismissal( ArgumentMatchers.any(), - ArgumentMatchers.anyInt() + ArgumentMatchers.anyInt(), ) ) .thenReturn(mFutureDismissalRunnable) } private fun getNotifRemoteViewsFactoryContainer( - featureFlags: FeatureFlags, + featureFlags: FeatureFlags ): NotifRemoteViewsFactoryContainer { return NotifRemoteViewsFactoryContainerImpl( featureFlags, @@ -285,6 +288,10 @@ class ExpandableNotificationRowBuilder( BigPictureLayoutInflaterFactory(), NotificationOptimizedLinearLayoutFactory(), { Mockito.mock(NotificationViewFlipperFactory::class.java) }, + NotificationRowIconViewInflaterFactory( + AppIconProviderImpl(context), + NotificationIconStyleProviderImpl(), + ), ) } @@ -293,7 +300,7 @@ class ExpandableNotificationRowBuilder( NotificationChannel( notification.channelId, notification.channelId, - NotificationManager.IMPORTANCE_DEFAULT + NotificationManager.IMPORTANCE_DEFAULT, ) channel.isBlockable = true val entry = @@ -321,7 +328,7 @@ class ExpandableNotificationRowBuilder( private fun generateRow( entry: NotificationEntry, - @InflationFlag extraInflationFlags: Int + @InflationFlag extraInflationFlags: Int, ): ExpandableNotificationRow { // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be // set, but we do not want to override an existing value that is needed by a specific test. @@ -329,7 +336,7 @@ class ExpandableNotificationRowBuilder( val rowInflaterTask = RowInflaterTask( mFakeSystemClock, - Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY) + Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY), ) val row = rowInflaterTask.inflateSynchronously(context, null, entry) @@ -364,7 +371,7 @@ class ExpandableNotificationRowBuilder( mSmartReplyController, featureFlags, Mockito.mock(IStatusBarService::class.java, STUB_ONLY), - Mockito.mock(UiEventLogger::class.java, STUB_ONLY) + Mockito.mock(UiEventLogger::class.java, STUB_ONLY), ) row.setAboveShelfChangedListener { aboveShelf: Boolean -> } mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt new file mode 100644 index 000000000000..08c6bbab6dd6 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.icon + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +val Kosmos.appIconProvider by Kosmos.Fixture { AppIconProviderImpl(applicationContext) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt new file mode 100644 index 000000000000..611c90a6f4e8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.icon + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.notificationIconStyleProvider by Kosmos.Fixture { NotificationIconStyleProviderImpl() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt new file mode 100644 index 000000000000..9c9673c3a924 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import com.android.systemui.kosmos.Kosmos +import org.mockito.kotlin.mock + +val Kosmos.mockStatusBarContentInsetsProvider by + Kosmos.Fixture { mock<StatusBarContentInsetsProvider>() } + +var Kosmos.statusBarContentInsetsProvider by Kosmos.Fixture { mockStatusBarContentInsetsProvider } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt index 1c7fd4817498..3a7ada2e61b8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope @@ -27,13 +28,14 @@ import com.android.systemui.statusbar.notification.domain.interactor.activeNotif import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor import com.android.systemui.statusbar.pipeline.shared.domain.interactor.collapsedStatusBarInteractor -val Kosmos.collapsedStatusBarViewModel: CollapsedStatusBarViewModel by +val Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by Kosmos.Fixture { - CollapsedStatusBarViewModelImpl( + HomeStatusBarViewModelImpl( collapsedStatusBarInteractor, lightsOutInteractor, activeNotificationsInteractor, keyguardTransitionInteractor, + keyguardInteractor, sceneInteractor, sceneContainerOcclusionInteractor, shadeInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt new file mode 100644 index 000000000000..14f4d75d5647 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.policy + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeBluetoothController by Kosmos.Fixture { FakeBluetoothController() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt index d4e9bfbd1500..282f5947636c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt @@ -17,8 +17,11 @@ package com.android.systemui.statusbar.policy import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController var Kosmos.configurationController: ConfigurationController by Kosmos.Fixture { fakeConfigurationController } val Kosmos.fakeConfigurationController: FakeConfigurationController by Kosmos.Fixture { FakeConfigurationController() } +val Kosmos.statusBarConfigurationController: StatusBarConfigurationController by + Kosmos.Fixture { fakeConfigurationController } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt new file mode 100644 index 000000000000..4876cd8a6086 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.policy + +import android.bluetooth.BluetoothAdapter +import com.android.internal.annotations.VisibleForTesting +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.systemui.statusbar.policy.BluetoothController.Callback +import java.io.PrintWriter +import java.util.Collections +import java.util.concurrent.Executor + +class FakeBluetoothController : BluetoothController { + + private var callbacks = mutableListOf<Callback>() + private var enabled = false + + override fun addCallback(listener: Callback) { + callbacks += listener + listener.onBluetoothStateChange(isBluetoothEnabled) + } + + override fun removeCallback(listener: Callback) { + callbacks -= listener + } + + override fun dump(pw: PrintWriter, args: Array<out String>) {} + + override fun isBluetoothSupported(): Boolean = false + + override fun isBluetoothEnabled(): Boolean = enabled + + override fun getBluetoothState(): Int = 0 + + override fun isBluetoothConnected(): Boolean = false + + override fun isBluetoothConnecting(): Boolean = false + + override fun isBluetoothAudioProfileOnly(): Boolean = false + + override fun isBluetoothAudioActive(): Boolean = false + + override fun getConnectedDeviceName(): String? = null + + override fun setBluetoothEnabled(enabled: Boolean) { + this.enabled = enabled + callbacks.forEach { it.onBluetoothStateChange(enabled) } + } + + override fun canConfigBluetooth(): Boolean = false + + override fun getConnectedDevices(): MutableList<CachedBluetoothDevice> = Collections.emptyList() + + override fun addOnMetadataChangedListener( + device: CachedBluetoothDevice?, + executor: Executor?, + listener: BluetoothAdapter.OnMetadataChangedListener?, + ) {} + + override fun removeOnMetadataChangedListener( + device: CachedBluetoothDevice?, + listener: BluetoothAdapter.OnMetadataChangedListener?, + ) {} + + /** Trigger the [Callback.onBluetoothDevicesChanged] method for all registered callbacks. */ + @VisibleForTesting + fun onBluetoothDevicesChanged() { + callbacks.forEach { it.onBluetoothDevicesChanged() } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt index 46a10532ea52..6be13be407d8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt @@ -2,13 +2,15 @@ package com.android.systemui.statusbar.policy import android.content.res.Configuration import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController import dagger.Binds import dagger.Module import javax.inject.Inject /** Fake implementation of [ConfigurationController] for tests. */ @SysUISingleton -class FakeConfigurationController @Inject constructor() : ConfigurationController { +class FakeConfigurationController @Inject constructor() : + ConfigurationController, StatusBarConfigurationController { private var listeners = mutableListOf<ConfigurationController.ConfigurationListener>() private var isRtl = false @@ -43,6 +45,7 @@ class FakeConfigurationController @Inject constructor() : ConfigurationControlle } override fun isLayoutRtl(): Boolean = isRtl + override fun getNightModeName(): String = "undefined" } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt index 10f328be12d2..bca13c6f502d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt @@ -18,10 +18,12 @@ package com.android.systemui.statusbar.window import android.content.Context import com.android.app.viewcapture.ViewCaptureAwareWindowManager +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController class FakeStatusBarWindowControllerFactory : StatusBarWindowController.Factory { override fun create( context: Context, viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + statusBarConfigurationController: StatusBarConfigurationController ) = FakeStatusBarWindowController() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowViewInflater.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowViewInflater.kt new file mode 100644 index 000000000000..138b4423d90f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowViewInflater.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.window + +import android.content.Context +import org.mockito.kotlin.mock + +class FakeStatusBarWindowViewInflater : StatusBarWindowViewInflater { + + val inflatedMockViews = mutableListOf<StatusBarWindowView>() + + override fun inflate(context: Context): StatusBarWindowView { + return mock<StatusBarWindowView>().also { inflatedMockViews += it } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt index 78caf93d4618..173e909e3b3f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt @@ -16,10 +16,31 @@ package com.android.systemui.statusbar.window +import android.content.testableContext +import android.view.windowManagerService +import com.android.app.viewcapture.viewCaptureAwareWindowManager +import com.android.systemui.fragments.fragmentService import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.phone.statusBarContentInsetsProvider +import com.android.systemui.statusbar.policy.statusBarConfigurationController +import java.util.Optional val Kosmos.fakeStatusBarWindowController by Kosmos.Fixture { FakeStatusBarWindowController() } +val Kosmos.statusBarWindowControllerImpl by + Kosmos.Fixture { + StatusBarWindowControllerImpl( + testableContext, + statusBarWindowViewInflater, + viewCaptureAwareWindowManager, + statusBarConfigurationController, + windowManagerService, + statusBarContentInsetsProvider, + fragmentService, + Optional.empty(), + ) + } + var Kosmos.statusBarWindowController: StatusBarWindowController by Kosmos.Fixture { fakeStatusBarWindowController } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowViewKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowViewKosmos.kt new file mode 100644 index 000000000000..e7cf83f80243 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowViewKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.window + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeStatusBarWindowViewInflater by Kosmos.Fixture { FakeStatusBarWindowViewInflater() } + +var Kosmos.statusBarWindowViewInflater: StatusBarWindowViewInflater by + Kosmos.Fixture { fakeStatusBarWindowViewInflater } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt index 2205a3b6e084..cbaf2bd65083 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt @@ -21,6 +21,9 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.settings.displayTracker import com.android.systemui.statusbar.commandQueue +val Kosmos.fakeStatusBarWindowStatePerDisplayRepository by + Kosmos.Fixture { FakeStatusBarWindowStatePerDisplayRepository() } + val Kosmos.fakeStatusBarWindowStateRepositoryStore by Kosmos.Fixture { FakeStatusBarWindowStateRepositoryStore() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/AudioSystemRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/AudioSystemRepositoryKosmos.kt new file mode 100644 index 000000000000..ec2bf24effbc --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/AudioSystemRepositoryKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.audioSystemRepository by Kosmos.Fixture { FakeAudioSystemRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSystemRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSystemRepository.kt new file mode 100644 index 000000000000..0ff4dec0e2ed --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSystemRepository.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.data.repository + +import com.android.settingslib.volume.data.repository.AudioSystemRepository + +class FakeAudioSystemRepository : AudioSystemRepository { + + override var isSingleVolume = false + + fun setIsSingleVolume(singleVolume: Boolean) { + isSingleVolume = singleVolume + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt index dd5bbf37c0f1..3bc920edd948 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.volume.panel.component.volume.domain.interactor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.volume.data.repository.audioSystemRepository import com.android.systemui.volume.domain.interactor.audioModeInteractor import com.android.systemui.volume.mediaOutputInteractor @@ -27,5 +28,6 @@ val Kosmos.audioSlidersInteractor by applicationCoroutineScope, mediaOutputInteractor, audioModeInteractor, + audioSystemRepository, ) } diff --git a/packages/Vcn/OWNERS b/packages/Vcn/OWNERS new file mode 100644 index 000000000000..ff2146e76b47 --- /dev/null +++ b/packages/Vcn/OWNERS @@ -0,0 +1,6 @@ +evitayan@google.com +nharold@google.com +benedictwong@google.com #{LAST_RESORT_SUGGESTION} +yangji@google.com #{LAST_RESORT_SUGGESTION} + +include platform/packages/modules/common:/MODULES_OWNERS
\ No newline at end of file diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index bfa801f30955..d9182010c1cb 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -124,6 +124,7 @@ java_library { ], libs: [ "framework-minus-apex.ravenwood", + "framework-configinfrastructure.ravenwood", "ravenwood-helper-libcore-runtime", ], sdk_version: "core_current", @@ -372,6 +373,7 @@ java_library { android_ravenwood_libgroup { name: "ravenwood-runtime", data: [ + ":system-build.prop", ":framework-res", ":ravenwood-empty-res", ":framework-platform-compat-config", @@ -394,6 +396,9 @@ android_ravenwood_libgroup { "icu4j-icudata-jarjar", "icu4j-icutzdata-jarjar", + // DeviceConfig + "framework-configinfrastructure.ravenwood", + // Provide runtime versions of utils linked in below "junit", "truth", diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp index 5cb1479514a3..1bea434f5b49 100644 --- a/ravenwood/Framework.bp +++ b/ravenwood/Framework.bp @@ -290,3 +290,57 @@ java_genrule { "core-icu4j-for-host.ravenwood.jar", ], } + +/////////////////////////////////// +// framework-configinfrastructure +/////////////////////////////////// + +java_genrule { + name: "framework-configinfrastructure.ravenwood-base", + tools: ["hoststubgen"], + cmd: "$(location hoststubgen) " + + "@$(location :ravenwood-standard-options) " + + + "--debug-log $(location framework-configinfrastructure.log) " + + "--stats-file $(location framework-configinfrastructure_stats.csv) " + + "--supported-api-list-file $(location framework-configinfrastructure_apis.csv) " + + "--gen-keep-all-file $(location framework-configinfrastructure_keep_all.txt) " + + "--gen-input-dump-file $(location framework-configinfrastructure_dump.txt) " + + + "--out-impl-jar $(location ravenwood.jar) " + + "--in-jar $(location :framework-configinfrastructure.impl{.jar}) " + + + "--policy-override-file $(location :ravenwood-common-policies) " + + "--policy-override-file $(location :framework-configinfrastructure-ravenwood-policies) ", + srcs: [ + ":framework-configinfrastructure.impl{.jar}", + + ":ravenwood-common-policies", + ":framework-configinfrastructure-ravenwood-policies", + ":ravenwood-standard-options", + ], + out: [ + "ravenwood.jar", + + // Following files are created just as FYI. + "framework-configinfrastructure_keep_all.txt", + "framework-configinfrastructure_dump.txt", + + "framework-configinfrastructure.log", + "framework-configinfrastructure_stats.csv", + "framework-configinfrastructure_apis.csv", + ], + visibility: ["//visibility:private"], +} + +java_genrule { + name: "framework-configinfrastructure.ravenwood", + defaults: ["ravenwood-internal-only-visibility-genrule"], + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-configinfrastructure.ravenwood-base{ravenwood.jar}", + ], + out: [ + "framework-configinfrastructure.ravenwood.jar", + ], +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index 5894476b9201..e2d73d1f1cb5 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -20,12 +20,14 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_R import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP; +import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRuntimePath; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.Instrumentation; import android.app.ResourcesManager; @@ -38,6 +40,7 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.ServiceManager; import android.os.SystemProperties; +import android.provider.DeviceConfig_host; import android.system.ErrnoException; import android.system.Os; import android.util.Log; @@ -81,6 +84,8 @@ public class RavenwoodRuntimeEnvironmentController { private static final String MAIN_THREAD_NAME = "RavenwoodMain"; private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop"; private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime"; + private static final String RAVENWOOD_BUILD_PROP = + getRavenwoodRuntimePath() + "ravenwood-data/build.prop"; /** * When enabled, attempt to dump all thread stacks just before we hit the @@ -158,7 +163,8 @@ public class RavenwoodRuntimeEnvironmentController { System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME)); // Do the basic set up for the android sysprops. - setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES); + RavenwoodSystemProperties.initialize(RAVENWOOD_BUILD_PROP); + setSystemProperties(null); // Make sure libandroid_runtime is loaded. RavenwoodNativeLoader.loadFrameworkNativeCode(); @@ -220,14 +226,9 @@ public class RavenwoodRuntimeEnvironmentController { ActivityManager.init$ravenwood(config.mCurrentUser); - final HandlerThread main; - if (config.mProvideMainThread) { - main = new HandlerThread(MAIN_THREAD_NAME); - main.start(); - Looper.setMainLooperForTest(main.getLooper()); - } else { - main = null; - } + final var main = new HandlerThread(MAIN_THREAD_NAME); + main.start(); + Looper.setMainLooperForTest(main.getLooper()); final boolean isSelfInstrumenting = Objects.equals(config.mTestPackageName, config.mTargetPackageName); @@ -319,22 +320,22 @@ public class RavenwoodRuntimeEnvironmentController { } sMockUiAutomation.dropShellPermissionIdentity(); - if (config.mProvideMainThread) { - Looper.getMainLooper().quit(); - Looper.clearMainLooperForTest(); - } + Looper.getMainLooper().quit(); + Looper.clearMainLooperForTest(); ActivityManager.reset$ravenwood(); LocalServices.removeAllServicesForTest(); ServiceManager.reset$ravenwood(); - setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES); + setSystemProperties(null); if (sOriginalIdentityToken != -1) { Binder.restoreCallingIdentity(sOriginalIdentityToken); } android.os.Process.reset$ravenwood(); + DeviceConfig_host.reset(); + try { ResourcesManager.setInstance(null); // Better structure needed. } catch (Exception e) { @@ -388,9 +389,10 @@ public class RavenwoodRuntimeEnvironmentController { /** * Set the current configuration to the actual SystemProperties. */ - private static void setSystemProperties(RavenwoodSystemProperties systemProperties) { + private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) { SystemProperties.clearChangeCallbacksForTest(); RavenwoodRuntimeNative.clearSystemProperties(); + if (systemProperties == null) systemProperties = new RavenwoodSystemProperties(); sProps = new RavenwoodSystemProperties(systemProperties, true); for (var entry : systemProperties.getValues().entrySet()) { RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue()); diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java index 446f819ad41b..1f6e11dd5cf2 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java @@ -152,7 +152,10 @@ public final class RavenwoodConfig { /** * Configure a "main" thread to be available for the duration of the test, as defined * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments. + * + * @deprecated */ + @Deprecated public Builder setProvideMainThread(boolean provideMainThread) { mConfig.mProvideMainThread = provideMainThread; return this; diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index 4196d8e22610..93a6806ed1f4 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -139,7 +139,10 @@ public final class RavenwoodRule implements TestRule { /** * Configure a "main" thread to be available for the duration of the test, as defined * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments. + * + * @deprecated */ + @Deprecated public Builder setProvideMainThread(boolean provideMainThread) { mBuilder.setProvideMainThread(provideMainThread); return this; diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java index f1e1ef672871..ced151927fdc 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java @@ -18,12 +18,94 @@ package android.platform.test.ravenwood; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_SYSPROP; +import com.android.ravenwood.common.RavenwoodCommonUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class RavenwoodSystemProperties { + private static final String TAG = "RavenwoodSystemProperties"; + + private static final Map<String, String> sDefaultValues = new HashMap<>(); + + private static final String[] PARTITIONS = { + "bootimage", + "odm", + "product", + "system", + "system_ext", + "vendor", + "vendor_dlkm", + }; + + /** + * More info about property file loading: system/core/init/property_service.cpp + * In the following logic, the only partition we would need to consider is "system", + * since we only read from system-build.prop + */ + static void initialize(String propFile) { + // Load all properties from build.prop + try { + Files.readAllLines(Path.of(propFile)).stream() + .map(String::trim) + .filter(s -> !s.startsWith("#")) + .map(s -> s.split("\\s*=\\s*", 2)) + .filter(a -> a.length == 2) + .forEach(a -> sDefaultValues.put(a[0], a[1])); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // If ro.product.${name} is not set, derive from ro.product.${partition}.${name} + // If ro.product.cpu.abilist* is not set, derive from ro.${partition}.product.cpu.abilist* + for (var entry : Set.copyOf(sDefaultValues.entrySet())) { + final String key; + if (entry.getKey().startsWith("ro.product.system.")) { + var name = entry.getKey().substring(18); + key = "ro.product." + name; + + } else if (entry.getKey().startsWith("ro.system.product.cpu.abilist")) { + var name = entry.getKey().substring(22); + key = "ro.product.cpu." + name; + } else { + continue; + } + if (!sDefaultValues.containsKey(key)) { + sDefaultValues.put(key, entry.getValue()); + } + } + + // Some other custom values + sDefaultValues.put("ro.board.first_api_level", "1"); + sDefaultValues.put("ro.product.first_api_level", "1"); + sDefaultValues.put("ro.soc.manufacturer", "Android"); + sDefaultValues.put("ro.soc.model", "Ravenwood"); + sDefaultValues.put(RAVENWOOD_SYSPROP, "1"); + + // Log all values + sDefaultValues.forEach((key, value) -> RavenwoodCommonUtils.log(TAG, key + "=" + value)); + + // Copy ro.product.* and ro.build.* to all partitions, just in case + // We don't want to log these because these are just a lot of duplicate values + for (var entry : Set.copyOf(sDefaultValues.entrySet())) { + var key = entry.getKey(); + if (key.startsWith("ro.product.") || key.startsWith("ro.build.")) { + var name = key.substring(3); + for (String partition : PARTITIONS) { + var newKey = "ro." + partition + "." + name; + if (!sDefaultValues.containsKey(newKey)) { + sDefaultValues.put(newKey, entry.getValue()); + } + } + } + } + } + private volatile boolean mIsImmutable; private final Map<String, String> mValues = new HashMap<>(); @@ -35,47 +117,15 @@ public class RavenwoodSystemProperties { private final Set<String> mKeyWritable = new HashSet<>(); public RavenwoodSystemProperties() { - // TODO: load these values from build.prop generated files - setValueForPartitions("product.brand", "Android"); - setValueForPartitions("product.device", "Ravenwood"); - setValueForPartitions("product.manufacturer", "Android"); - setValueForPartitions("product.model", "Ravenwood"); - setValueForPartitions("product.name", "Ravenwood"); - - setValueForPartitions("product.cpu.abilist", "x86_64"); - setValueForPartitions("product.cpu.abilist32", ""); - setValueForPartitions("product.cpu.abilist64", "x86_64"); - - setValueForPartitions("build.date", "Thu Jan 01 00:00:00 GMT 2024"); - setValueForPartitions("build.date.utc", "1704092400"); - setValueForPartitions("build.id", "MAIN"); - setValueForPartitions("build.tags", "dev-keys"); - setValueForPartitions("build.type", "userdebug"); - setValueForPartitions("build.version.all_codenames", "REL"); - setValueForPartitions("build.version.codename", "REL"); - setValueForPartitions("build.version.incremental", "userdebug.ravenwood.20240101"); - setValueForPartitions("build.version.known_codenames", "REL"); - setValueForPartitions("build.version.release", "14"); - setValueForPartitions("build.version.release_or_codename", "VanillaIceCream"); - setValueForPartitions("build.version.sdk", "34"); - - setValue("ro.board.first_api_level", "1"); - setValue("ro.product.first_api_level", "1"); - - setValue("ro.soc.manufacturer", "Android"); - setValue("ro.soc.model", "Ravenwood"); - - setValue("ro.debuggable", "1"); - - setValue(RAVENWOOD_SYSPROP, "1"); + mValues.putAll(sDefaultValues); } /** Copy constructor */ public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) { - this.mKeyReadable.addAll(source.mKeyReadable); - this.mKeyWritable.addAll(source.mKeyWritable); - this.mValues.putAll(source.mValues); - this.mIsImmutable = immutable; + mKeyReadable.addAll(source.mKeyReadable); + mKeyWritable.addAll(source.mKeyWritable); + mValues.putAll(source.mValues); + mIsImmutable = immutable; } public Map<String, String> getValues() { @@ -123,36 +173,12 @@ public class RavenwoodSystemProperties { return mKeyWritable.contains(key); } - private static final String[] PARTITIONS = { - "bootimage", - "odm", - "product", - "system", - "system_ext", - "vendor", - "vendor_dlkm", - }; - private void ensureNotImmutable() { if (mIsImmutable) { throw new RuntimeException("Unable to update immutable instance"); } } - /** - * Set the given property for all possible partitions where it could be defined. For - * example, the value of {@code ro.build.type} is typically also mirrored under - * {@code ro.system.build.type}, etc. - */ - private void setValueForPartitions(String key, String value) { - ensureNotImmutable(); - - setValue("ro." + key, value); - for (String partition : PARTITIONS) { - setValue("ro." + partition + "." + key, value); - } - } - public void setValue(String key, Object value) { ensureNotImmutable(); @@ -195,11 +221,4 @@ public class RavenwoodSystemProperties { return key; } } - - /** - * Return an immutable, default instance. - */ - // Create a default instance, and make an immutable copy of it. - public static final RavenwoodSystemProperties DEFAULT_VALUES = - new RavenwoodSystemProperties(new RavenwoodSystemProperties(), true); } diff --git a/ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java b/ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java new file mode 100644 index 000000000000..9c2188fd377a --- /dev/null +++ b/ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.provider; + +public class DeviceConfig_host { + + /** + * Called by Ravenwood runtime to reset all local changes. + */ + public static void reset() { + RavenwoodConfigDataStore.getInstance().clearAll(); + } + + /** + * Called by {@link DeviceConfig#newDataStore()} + */ + public static DeviceConfigDataStore newDataStore() { + return RavenwoodConfigDataStore.getInstance(); + } +} diff --git a/ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java b/ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java new file mode 100644 index 000000000000..4bc3de79fe6b --- /dev/null +++ b/ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.provider; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.provider.DeviceConfig.BadConfigException; +import android.provider.DeviceConfig.MonitorCallback; +import android.provider.DeviceConfig.Properties; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * {@link DeviceConfigDataStore} used only on Ravenwood. + * + * TODO(b/368591527) Support monitor callback related features + * TODO(b/368591527) Support "default" related features + */ +final class RavenwoodConfigDataStore implements DeviceConfigDataStore { + private static final RavenwoodConfigDataStore sInstance = new RavenwoodConfigDataStore(); + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private int mSyncDisabledMode = DeviceConfig.SYNC_DISABLED_MODE_NONE; + + @GuardedBy("mLock") + private final HashMap<String, HashMap<String, String>> mStore = new HashMap<>(); + + private record ObserverInfo(String namespace, ContentObserver observer) { + } + + @GuardedBy("mLock") + private final ArrayList<ObserverInfo> mObservers = new ArrayList<>(); + + static RavenwoodConfigDataStore getInstance() { + return sInstance; + } + + private static void shouldNotBeCalled() { + throw new RuntimeException("shouldNotBeCalled"); + } + + void clearAll() { + synchronized (mLock) { + mSyncDisabledMode = DeviceConfig.SYNC_DISABLED_MODE_NONE; + mStore.clear(); + } + } + + @GuardedBy("mLock") + private HashMap<String, String> getNamespaceLocked(@NonNull String namespace) { + Objects.requireNonNull(namespace); + return mStore.computeIfAbsent(namespace, k -> new HashMap<>()); + } + + /** {@inheritDoc} */ + @NonNull + @Override + public Map<String, String> getAllProperties() { + synchronized (mLock) { + var ret = new HashMap<String, String>(); + + for (var namespaceAndMap : mStore.entrySet()) { + for (var nameAndValue : namespaceAndMap.getValue().entrySet()) { + ret.put(namespaceAndMap.getKey() + "/" + nameAndValue.getKey(), + nameAndValue.getValue()); + } + } + return ret; + } + } + + /** {@inheritDoc} */ + @NonNull + @Override + public Properties getProperties(@NonNull String namespace, @NonNull String... names) { + Objects.requireNonNull(namespace); + + synchronized (mLock) { + var namespaceMap = getNamespaceLocked(namespace); + + if (names.length == 0) { + return new Properties(namespace, Map.copyOf(namespaceMap)); + } else { + var map = new HashMap<String, String>(); + for (var name : names) { + Objects.requireNonNull(name); + map.put(name, namespaceMap.get(name)); + } + return new Properties(namespace, map); + } + } + } + + /** {@inheritDoc} */ + @Override + public boolean setProperties(@NonNull Properties properties) throws BadConfigException { + Objects.requireNonNull(properties); + + synchronized (mLock) { + var namespaceMap = getNamespaceLocked(properties.getNamespace()); + for (var kv : properties.getPropertyValues().entrySet()) { + namespaceMap.put( + Objects.requireNonNull(kv.getKey()), + Objects.requireNonNull(kv.getValue()) + ); + } + notifyObserversLock(properties.getNamespace(), + properties.getKeyset().toArray(new String[0])); + } + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean setProperty(@NonNull String namespace, @NonNull String name, + @Nullable String value, boolean makeDefault) { + Objects.requireNonNull(namespace); + Objects.requireNonNull(name); + + synchronized (mLock) { + var namespaceMap = getNamespaceLocked(namespace); + namespaceMap.put(name, value); + + // makeDefault not supported. + notifyObserversLock(namespace, new String[]{name}); + } + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean deleteProperty(@NonNull String namespace, @NonNull String name) { + Objects.requireNonNull(namespace); + Objects.requireNonNull(name); + + synchronized (mLock) { + var namespaceMap = getNamespaceLocked(namespace); + if (namespaceMap.remove(name) != null) { + notifyObserversLock(namespace, new String[]{name}); + } + } + return true; + } + + /** {@inheritDoc} */ + @Override + public void resetToDefaults(int resetMode, @Nullable String namespace) { + // not supported in DeviceConfig.java + shouldNotBeCalled(); + } + + /** {@inheritDoc} */ + @Override + public void setSyncDisabledMode(int syncDisabledMode) { + synchronized (mLock) { + mSyncDisabledMode = syncDisabledMode; + } + } + + /** {@inheritDoc} */ + @Override + public int getSyncDisabledMode() { + synchronized (mLock) { + return mSyncDisabledMode; + } + } + + /** {@inheritDoc} */ + @Override + public void setMonitorCallback(@NonNull ContentResolver resolver, @NonNull Executor executor, + @NonNull MonitorCallback callback) { + // not supported in DeviceConfig.java + shouldNotBeCalled(); + } + + /** {@inheritDoc} */ + @Override + public void clearMonitorCallback(@NonNull ContentResolver resolver) { + // not supported in DeviceConfig.java + shouldNotBeCalled(); + } + + /** {@inheritDoc} */ + @Override + public void registerContentObserver(@NonNull String namespace, boolean notifyForescendants, + ContentObserver contentObserver) { + synchronized (mLock) { + mObservers.add(new ObserverInfo( + Objects.requireNonNull(namespace), + Objects.requireNonNull(contentObserver) + )); + } + } + + /** {@inheritDoc} */ + @Override + public void unregisterContentObserver(@NonNull ContentObserver contentObserver) { + synchronized (mLock) { + for (int i = mObservers.size() - 1; i >= 0; i--) { + if (mObservers.get(i).observer == contentObserver) { + mObservers.remove(i); + } + } + } + } + + private static final Uri CONTENT_URI = Uri.parse("content://settings/config"); + + @GuardedBy("mLock") + private void notifyObserversLock(@NonNull String namespace, String[] keys) { + var urib = CONTENT_URI.buildUpon().appendPath(namespace); + for (var key : keys) { + urib.appendEncodedPath(key); + } + var uri = urib.build(); + + final var copy = List.copyOf(mObservers); + new Handler(Looper.getMainLooper()).post(() -> { + for (int i = copy.size() - 1; i >= 0; i--) { + if (copy.get(i).namespace.equals(namespace)) { + copy.get(i).observer.dispatchChange(false, uri); + } + } + }); + } +} diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp index ac499b966afe..d7f4b3e2955d 100644 --- a/ravenwood/tests/bivalenttest/Android.bp +++ b/ravenwood/tests/bivalenttest/Android.bp @@ -54,34 +54,36 @@ android_ravenwood_test { auto_gen_config: true, } -// TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture +android_test { + name: "RavenwoodBivalentTest_device", -// android_test { -// name: "RavenwoodBivalentTest_device", -// -// srcs: [ -// "test/**/*.java", -// ], -// static_libs: [ -// "junit", -// "truth", -// -// "androidx.annotation_annotation", -// "androidx.test.ext.junit", -// "androidx.test.rules", -// -// "junit-params", -// "platform-parametric-runner-lib", -// -// "ravenwood-junit", -// ], -// jni_libs: [ -// "libravenwoodbivalenttest_jni", -// ], -// test_suites: [ -// "device-tests", -// ], -// optimize: { -// enabled: false, -// }, -// } + srcs: [ + "test/**/*.java", + ], + // TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture + exclude_srcs: [ + "test/**/ravenizer/*.java", + ], + static_libs: [ + "junit", + "truth", + + "androidx.annotation_annotation", + "androidx.test.ext.junit", + "androidx.test.rules", + + "junit-params", + "platform-parametric-runner-lib", + + "ravenwood-junit", + ], + jni_libs: [ + "libravenwoodbivalenttest_jni", + ], + test_suites: [ + "device-tests", + ], + optimize: { + enabled: false, + }, +} diff --git a/ravenwood/minimum-test/Android.bp b/ravenwood/tests/minimum-test/Android.bp index e4ed3d5b0b73..e4ed3d5b0b73 100644 --- a/ravenwood/minimum-test/Android.bp +++ b/ravenwood/tests/minimum-test/Android.bp diff --git a/ravenwood/minimum-test/README.md b/ravenwood/tests/minimum-test/README.md index 6b0abe968053..6b0abe968053 100644 --- a/ravenwood/minimum-test/README.md +++ b/ravenwood/tests/minimum-test/README.md diff --git a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java index 30abaa2e7d38..30abaa2e7d38 100644 --- a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java +++ b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java diff --git a/ravenwood/mockito/Android.bp b/ravenwood/tests/mockito/Android.bp index d91537bbc334..d91537bbc334 100644 --- a/ravenwood/mockito/Android.bp +++ b/ravenwood/tests/mockito/Android.bp diff --git a/ravenwood/mockito/AndroidManifest.xml b/ravenwood/tests/mockito/AndroidManifest.xml index 15f0a2934b5f..15f0a2934b5f 100644 --- a/ravenwood/mockito/AndroidManifest.xml +++ b/ravenwood/tests/mockito/AndroidManifest.xml diff --git a/ravenwood/mockito/AndroidTest.xml b/ravenwood/tests/mockito/AndroidTest.xml index 5ba9b1ff2cd8..5ba9b1ff2cd8 100644 --- a/ravenwood/mockito/AndroidTest.xml +++ b/ravenwood/tests/mockito/AndroidTest.xml diff --git a/ravenwood/mockito/README.md b/ravenwood/tests/mockito/README.md index 4ceb795fe14a..4ceb795fe14a 100644 --- a/ravenwood/mockito/README.md +++ b/ravenwood/tests/mockito/README.md diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java index d566977bd15c..d566977bd15c 100644 --- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java +++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java index aa2b7611da37..aa2b7611da37 100644 --- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java +++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java index fcc6c9cc447d..fcc6c9cc447d 100644 --- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java +++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java diff --git a/ravenwood/resapk_test/Android.bp b/ravenwood/tests/resapk_test/Android.bp index c14576550f78..c14576550f78 100644 --- a/ravenwood/resapk_test/Android.bp +++ b/ravenwood/tests/resapk_test/Android.bp diff --git a/ravenwood/resapk_test/apk/Android.bp b/ravenwood/tests/resapk_test/apk/Android.bp index 10ed5e2f8410..10ed5e2f8410 100644 --- a/ravenwood/resapk_test/apk/Android.bp +++ b/ravenwood/tests/resapk_test/apk/Android.bp diff --git a/ravenwood/resapk_test/apk/AndroidManifest.xml b/ravenwood/tests/resapk_test/apk/AndroidManifest.xml index f34d8b2f4e81..f34d8b2f4e81 100644 --- a/ravenwood/resapk_test/apk/AndroidManifest.xml +++ b/ravenwood/tests/resapk_test/apk/AndroidManifest.xml diff --git a/ravenwood/resapk_test/apk/res/values/strings.xml b/ravenwood/tests/resapk_test/apk/res/values/strings.xml index 23d4c0f21007..23d4c0f21007 100644 --- a/ravenwood/resapk_test/apk/res/values/strings.xml +++ b/ravenwood/tests/resapk_test/apk/res/values/strings.xml diff --git a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java index e547114bbe40..e547114bbe40 100644 --- a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java +++ b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java diff --git a/ravenwood/runtime-test/Android.bp b/ravenwood/tests/runtime-test/Android.bp index 410292001670..410292001670 100644 --- a/ravenwood/runtime-test/Android.bp +++ b/ravenwood/tests/runtime-test/Android.bp diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java index 633ed4e9d10a..633ed4e9d10a 100644 --- a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java index c2230c739ccf..c2230c739ccf 100644 --- a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java diff --git a/ravenwood/services-test/Android.bp b/ravenwood/tests/services-test/Android.bp index 39858f05e80d..39858f05e80d 100644 --- a/ravenwood/services-test/Android.bp +++ b/ravenwood/tests/services-test/Android.bp diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java index f833782bc8bb..f833782bc8bb 100644 --- a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java +++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java index b3d3963270ee..b3d3963270ee 100644 --- a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java +++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java diff --git a/ravenwood/tools/hoststubgen/.gitignore b/ravenwood/tools/hoststubgen/.gitignore new file mode 100644 index 000000000000..82158c9b25e3 --- /dev/null +++ b/ravenwood/tools/hoststubgen/.gitignore @@ -0,0 +1,4 @@ +framework-all-stub-out +out/ +*-out/ +*.log diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/ravenwood/tools/hoststubgen/Android.bp index a5ff4964b0a4..a5ff4964b0a4 100644 --- a/tools/hoststubgen/hoststubgen/Android.bp +++ b/ravenwood/tools/hoststubgen/Android.bp diff --git a/tools/hoststubgen/README.md b/ravenwood/tools/hoststubgen/README.md index 1a895dc7dfce..615e7671bea1 100644 --- a/tools/hoststubgen/README.md +++ b/ravenwood/tools/hoststubgen/README.md @@ -11,7 +11,7 @@ used at runtime. - HostStubGen itself is design to be agnostic to Android. It doesn't use any Android APIs (hidden or not). But it may use Android specific knowledge -- e.g. as of now, -AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classes. +AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classes. - `test-tiny-framework/` contains basic tests that are agnostic to Android. @@ -20,19 +20,16 @@ AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classe ## Directories and files -- `hoststubgen/` - Contains source code of the "hoststubgen" tool and relevant code +- `src/` - - `src/` + HostStubGen tool source code. - HostStubGen tool source code. +- `annotations-src/` See `Android.bp`. +- `helper-framework-buildtime-src/` See `Android.bp`. +- `helper-framework-runtime-src/` See `Android.bp`. +- `helper-runtime-src/` See `Android.bp`. - - `annotations-src/` See `Android.bp`. - - `helper-framework-buildtime-src/` See `Android.bp`. - - `helper-framework-runtime-src/` See `Android.bp`. - - `helper-runtime-src/` See `Android.bp`. - - - `test-tiny-framework/` See `README.md` in it. +- `test-tiny-framework/` See `README.md` in it. - `scripts` - `dump-jar.sh` @@ -78,4 +75,4 @@ $ ./scripts/run-all-tests.sh - At some point, we can move or delete all Android specific code to `frameworks/base/ravenwood`. - `helper-framework-*-src` should be moved to `frameworks/base/ravenwood` - - `test-framework` should be deleted.
\ No newline at end of file + - `test-framework` should be deleted. diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java index a774336a897c..a774336a897c 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java index 501fd652145e..501fd652145e 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java index 06ad1c266a14..06ad1c266a14 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java index bc9471b84b97..bc9471b84b97 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java index 28ad236a66f3..28ad236a66f3 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java index 46e5078fb05d..46e5078fb05d 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java index eec72269e0d3..eec72269e0d3 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java index 510a67e0aaed..510a67e0aaed 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java index cd1bef4be505..cd1bef4be505 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java index 3d1ddea2cbb7..3d1ddea2cbb7 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java index b10f0ff1a4b1..b10f0ff1a4b1 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java diff --git a/tools/hoststubgen/common.sh b/ravenwood/tools/hoststubgen/common.sh index b49ee39a3142..b49ee39a3142 100644 --- a/tools/hoststubgen/common.sh +++ b/ravenwood/tools/hoststubgen/common.sh diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/ravenwood/tools/hoststubgen/framework-policy-override.txt index af3789e270a4..af3789e270a4 100644 --- a/tools/hoststubgen/hoststubgen/framework-policy-override.txt +++ b/ravenwood/tools/hoststubgen/framework-policy-override.txt diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java index b01710347537..b01710347537 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java index 18ef1bab203e..18ef1bab203e 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java index 99e38c0b1725..99e38c0b1725 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java index 4933cf8784d9..4933cf8784d9 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java index c54c2c111229..c54c2c111229 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java index 29f7be008eef..29f7be008eef 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java index 78fd8f7f960a..78fd8f7f960a 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt index 001943c18d6b..001943c18d6b 100644 --- a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt +++ b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt diff --git a/tools/hoststubgen/hoststubgen/invoketest/Android.bp b/ravenwood/tools/hoststubgen/invoketest/Android.bp index 7e90e421a1f9..7e90e421a1f9 100644 --- a/tools/hoststubgen/hoststubgen/invoketest/Android.bp +++ b/ravenwood/tools/hoststubgen/invoketest/Android.bp diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/ravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh index 084448d0a797..084448d0a797 100755 --- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh +++ b/ravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh diff --git a/tools/hoststubgen/hoststubgen/jarjar-rules.txt b/ravenwood/tools/hoststubgen/jarjar-rules.txt index b1f2fc21c8e9..b1f2fc21c8e9 100644 --- a/tools/hoststubgen/hoststubgen/jarjar-rules.txt +++ b/ravenwood/tools/hoststubgen/jarjar-rules.txt diff --git a/tools/hoststubgen/scripts/Android.bp b/ravenwood/tools/hoststubgen/scripts/Android.bp index b1ba07ec540d..b1ba07ec540d 100644 --- a/tools/hoststubgen/scripts/Android.bp +++ b/ravenwood/tools/hoststubgen/scripts/Android.bp diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh b/ravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh index c3605a9ffaa5..c3605a9ffaa5 100755 --- a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh +++ b/ravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh diff --git a/tools/hoststubgen/scripts/dump-jar b/ravenwood/tools/hoststubgen/scripts/dump-jar index 87652451359d..87652451359d 100755 --- a/tools/hoststubgen/scripts/dump-jar +++ b/ravenwood/tools/hoststubgen/scripts/dump-jar diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt index f59e143c1e4e..f59e143c1e4e 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt index 6d8d7b768b91..6d8d7b768b91 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt index a218c5599553..a218c5599553 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt index 4bcee409aaec..4bcee409aaec 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt index 85064661cd2b..85064661cd2b 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt index 55e853e3e2fb..55e853e3e2fb 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt index 9045db210495..9045db210495 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt index 10179eefcb95..10179eefcb95 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt index a02082d12934..a02082d12934 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt index e2647eb13ed3..e2647eb13ed3 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt index 5e4e70f0cbaa..5e4e70f0cbaa 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt index 6b360b79c327..6b360b79c327 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt index 36adf0626415..36adf0626415 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt index f8bb526d0a86..f8bb526d0a86 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt index be3c59c80152..be3c59c80152 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt index d771003a955d..d771003a955d 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt index b8b0d8a31268..b8b0d8a31268 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt index 2f2f81b05ad1..2f2f81b05ad1 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt index b10165b835f2..b10165b835f2 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt index c5a2f9ff5e96..c5a2f9ff5e96 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt index 474da6dfa1b9..474da6dfa1b9 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt index 59fa464a7212..59fa464a7212 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt index 00e7d77fa6e7..00e7d77fa6e7 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt index f99ce906240a..f99ce906240a 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt index c67e6714d4c2..c67e6714d4c2 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt index 18a1e16bcf3a..18a1e16bcf3a 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt index fd7474b55fa6..fd7474b55fa6 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt index caf80ebec0c9..caf80ebec0c9 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt index d45f41407a52..d45f41407a52 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt index a78c6552b8d0..a78c6552b8d0 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt index d6aa7617fd59..d6aa7617fd59 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt index 1b3d79cddb8e..1b3d79cddb8e 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt index 261ef59c45c7..261ef59c45c7 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt index 55d0c0e555f1..55d0c0e555f1 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt index dc4f26bdda34..dc4f26bdda34 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt index 567a69e43b58..567a69e43b58 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt index e90ecd7ef678..e90ecd7ef678 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp index ba2c869adfe8..1570549ec27d 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp @@ -16,7 +16,7 @@ java_library { static_libs: [ "hoststubgen-annotations", ], - visibility: ["//frameworks/base/tools/hoststubgen:__subpackages__"], + visibility: ["//frameworks/base/ravenwood/tools/hoststubgen:__subpackages__"], } // Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules. diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml b/ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml index 84aad69c33bc..84aad69c33bc 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/ravenwood/tools/hoststubgen/test-tiny-framework/README.md index 344b4e953b23..344b4e953b23 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/README.md diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt index de4cb0c536c1..de4cb0c536c1 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh index 3726ca972564..3726ca972564 100755 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt index 103e152c7e39..103e152c7e39 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt index eeec554e954c..eeec554e954c 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt index 0f8af92dc486..0f8af92dc486 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt index 3c138d21b75d..3c138d21b75d 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh index 80ebf3adab3d..80ebf3adab3d 100755 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py index cee29dcd1d59..cee29dcd1d59 100755 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java index 0a07c2b91fc3..0a07c2b91fc3 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java index b1bedf4b6853..b1bedf4b6853 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java index 3415deb957ed..3415deb957ed 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java index f734790c8dd9..f734790c8dd9 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java index e83163edb5e5..e83163edb5e5 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java index 3df21d9a5647..3df21d9a5647 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java index cc665de9cd01..cc665de9cd01 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java index f833ad814513..f833ad814513 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java index c023169b5601..c023169b5601 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java index f7cae7d255fe..f7cae7d255fe 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java index ec1efba99c77..ec1efba99c77 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java index 1ca653ec7da6..1ca653ec7da6 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java index 57c69a336654..57c69a336654 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java index 04a551c8c46e..04a551c8c46e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java index c7a29a1cc0f9..c7a29a1cc0f9 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java index c1ea2ee59fbb..c1ea2ee59fbb 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java index 941fcff31d8e..941fcff31d8e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java index 707bc0ebb4db..707bc0ebb4db 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java index 8319ced6109a..8319ced6109a 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java index 6a52e4401b45..6a52e4401b45 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java index 1374a288f7aa..1374a288f7aa 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java index 361a7fd04842..361a7fd04842 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java index 716595a44243..716595a44243 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java index 03c9e2a7b5bb..03c9e2a7b5bb 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java index 3ca8f1f172bd..3ca8f1f172bd 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java index a6c14f09b680..a6c14f09b680 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java index 2e353709fd1e..2e353709fd1e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java index fe4cee64b2f9..fe4cee64b2f9 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java index 12012fcdf6ca..12012fcdf6ca 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java index 8d48ee6f6858..8d48ee6f6858 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java index 6748430a8e6f..6748430a8e6f 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java index 58aa5c3b74eb..58aa5c3b74eb 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java index c1c3d624b126..c1c3d624b126 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java index 398b56975f1c..398b56975f1c 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java index 44cbd8f9bffa..44cbd8f9bffa 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java index 42355a34b65c..42355a34b65c 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java index 09c80992e450..09c80992e450 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java index 0806a478d756..0806a478d756 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java index eaa8528a3f31..eaa8528a3f31 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java index 778c5aaf27f0..778c5aaf27f0 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java index 493f7c83c0f0..493f7c83c0f0 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java index 2aa1de18b7f4..2aa1de18b7f4 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java index d9eae0934c42..d9eae0934c42 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java index 9ee42836ac9a..9ee42836ac9a 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java index 50ec2cbc9c6e..50ec2cbc9c6e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java index 3f3659644a80..3f3659644a80 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java index 960060c8a036..960060c8a036 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java index c678eaa789b0..c678eaa789b0 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java index 1cff484c3cd8..1cff484c3cd8 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java index 84e7173c71b8..84e7173c71b8 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java index fa5866451e83..fa5866451e83 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java index 92f41ac63cdb..92f41ac63cdb 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java index 1ae049371229..1ae049371229 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java index 68673dc2a5b8..68673dc2a5b8 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java index 1816b383f6f7..1816b383f6f7 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt index 5b2795c4cff2..5b2795c4cff2 100644 --- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt +++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt index d4e75d43a54a..d4e75d43a54a 100644 --- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt +++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt index 081d03909926..081d03909926 100644 --- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt +++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt index 75e2536a98fa..75e2536a98fa 100644 --- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt +++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt diff --git a/services/Android.bp b/services/Android.bp index 653cd3c3b680..f04c692c12d0 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -195,7 +195,17 @@ soong_config_module_type { module_type: "java_library", config_namespace: "system_services", bool_variables: ["without_vibrator"], - properties: ["vintf_fragments"], + properties: ["vintf_fragment_modules"], +} + +vintf_fragment { + name: "manifest_services.xml", + src: "manifest_services.xml", +} + +vintf_fragment { + name: "manifest_services_android.frameworks.vibrator.xml", + src: "manifest_services_android.frameworks.vibrator.xml", } system_java_library { @@ -264,11 +274,11 @@ system_java_library { soong_config_variables: { without_vibrator: { - vintf_fragments: [ + vintf_fragment_modules: [ "manifest_services.xml", ], conditions_default: { - vintf_fragments: [ + vintf_fragment_modules: [ "manifest_services.xml", "manifest_services_android.frameworks.vibrator.xml", ], diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS index 0e35a409c82c..4e1175034b5b 100644 --- a/services/accessibility/OWNERS +++ b/services/accessibility/OWNERS @@ -2,10 +2,11 @@ # Android Accessibility Framework owners danielnorman@google.com -aarmaly@google.com chunkulin@google.com fuego@google.com sallyyuen@google.com +yingleiw@google.com +caseyburkhardt@google.com # Android Accessibility members who have OWNERS but should not be sent # day-to-day changes for code review: diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 034127c0420e..7057cc361a1a 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -45,6 +45,16 @@ flag { } flag { + name: "clear_shortcuts_when_activity_updates_to_service" + namespace: "accessibility" + description: "When an a11y activity is updated to an a11y service, clears the associated shortcuts so that we don't skip the AccessibilityServiceWarning." + bug: "358092445" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "compute_window_changes_on_a11y_v2" namespace: "accessibility" description: "Computes accessibility window changes in accessibility instead of wm package." @@ -114,10 +124,13 @@ flag { } flag { - name: "enable_magnification_follows_mouse" + name: "enable_magnification_follows_mouse_bugfix" namespace: "accessibility" description: "Whether to enable mouse following for fullscreen magnification" bug: "354696546" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 1451dfaa7964..ec8908bc7c91 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2513,6 +2513,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState, List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos) { if (!parsedAccessibilityShortcutInfos.equals(userState.mInstalledShortcuts)) { + if (Flags.clearShortcutsWhenActivityUpdatesToService()) { + List<String> componentNames = userState.mInstalledShortcuts.stream() + .filter(a11yActivity -> + !parsedAccessibilityShortcutInfos.contains(a11yActivity)) + .map(a11yActivity -> a11yActivity.getComponentName().flattenToString()) + .toList(); + if (!componentNames.isEmpty()) { + enableShortcutsForTargets( + /* enable= */ false, UserShortcutType.ALL, + componentNames, userState.mUserId); + } + } + userState.mInstalledShortcuts.clear(); userState.mInstalledShortcuts.addAll(parsedAccessibilityShortcutInfos); userState.updateTileServiceMapForAccessibilityActivityLocked(); diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index a19fdddea49c..963334b07ea6 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -345,7 +345,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @Override void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - if (Flags.enableMagnificationFollowsMouse()) { + if (Flags.enableMagnificationFollowsMouseBugfix()) { if (mFullScreenMagnificationController.isActivated(mDisplayId)) { // TODO(b/354696546): Allow mouse/stylus to activate whichever display they are // over, rather than only interacting with the current display. @@ -1206,7 +1206,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH protected void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - if (Flags.enableMagnificationFollowsMouse() + if (Flags.enableMagnificationFollowsMouseBugfix() && !event.isFromSource(SOURCE_TOUCHSCREEN)) { // Only touch events need to be cached and sent later. return; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java index 446123f07f64..fa86ba39bb1a 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java @@ -146,7 +146,8 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo } break; case SOURCE_MOUSE: case SOURCE_STYLUS: { - if (magnificationShortcutExists() && Flags.enableMagnificationFollowsMouse()) { + if (magnificationShortcutExists() + && Flags.enableMagnificationFollowsMouseBugfix()) { handleMouseOrStylusEvent(event, rawEvent, policyFlags); } } diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java index c3b7087a44c3..44ae1d1fbbbf 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java +++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java @@ -31,7 +31,8 @@ public final class AppFunctionExecutors { /* maxConcurrency= */ Runtime.getRuntime().availableProcessors(), /* keepAliveTime= */ 0L, /* unit= */ TimeUnit.SECONDS, - /* workQueue= */ new LinkedBlockingQueue<>()); + /* workQueue= */ new LinkedBlockingQueue<>(), + new NamedThreadFactory("AppFunctionExecutors")); private AppFunctionExecutors() {} } diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java index 5d574083b326..89f14b09d397 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java @@ -68,7 +68,10 @@ import com.android.server.SystemService.TargetUser; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collections; +import java.util.Map; import java.util.Objects; +import java.util.WeakHashMap; import java.util.concurrent.CompletionException; import java.util.concurrent.Executor; @@ -81,7 +84,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { private final ServiceHelper mInternalServiceHelper; private final ServiceConfig mServiceConfig; private final Context mContext; - private final Object mLock = new Object(); + private final Map<String, Object> mLocks = new WeakHashMap<>(); + public AppFunctionManagerServiceImpl(@NonNull Context context) { this( @@ -225,12 +229,6 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { .thenAccept( canExecute -> { if (!canExecute) { - safeExecuteAppFunctionCallback.onResult( - ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_DENIED, - "Caller does not have permission to execute the" - + " appfunction", - /* extras= */ null)); throw new SecurityException( "Caller does not have permission to execute the" + " appfunction"); @@ -322,9 +320,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { THREAD_POOL_EXECUTOR.execute( () -> { try { - // TODO(357551503): Instead of holding a global lock, hold a per-package - // lock. - synchronized (mLock) { + synchronized (getLockForPackage(callingPackage)) { setAppFunctionEnabledInternalLocked( callingPackage, functionIdentifier, userHandle, enabledState); } @@ -352,7 +348,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { * process. */ @WorkerThread - @GuardedBy("mLock") + @GuardedBy("getLockForPackage(callingPackage)") private void setAppFunctionEnabledInternalLocked( @NonNull String callingPackage, @NonNull String functionIdentifier, @@ -547,6 +543,26 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { }); } } + /** + * Retrieves the lock object associated with the given package name. + * + * This method returns the lock object from the {@code mLocks} map if it exists. + * If no lock is found for the given package name, a new lock object is created, + * stored in the map, and returned. + */ + @VisibleForTesting + @NonNull + Object getLockForPackage(String callingPackage) { + // Synchronized the access to mLocks to prevent race condition. + synchronized (mLocks) { + // By using a WeakHashMap, we allow the garbage collector to reclaim memory by removing + // entries associated with unused callingPackage keys. Therefore, we remove the null + // values before getting/computing a new value. The goal is to not let the size of this + // map grow without an upper bound. + mLocks.values().removeAll(Collections.singleton(null)); // Remove null values + return mLocks.computeIfAbsent(callingPackage, k -> new Object()); + } + } private static class AppFunctionMetadataObserver implements ObserverCallback { @Nullable private final MetadataSyncAdapter mPerUserMetadataSyncAdapter; diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java index 96be76975e9d..cc73288cdbfa 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java +++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java @@ -84,7 +84,9 @@ public class MetadataSyncAdapter { @NonNull PackageManager packageManager, @NonNull AppSearchManager appSearchManager) { mPackageManager = Objects.requireNonNull(packageManager); mAppSearchManager = Objects.requireNonNull(appSearchManager); - mExecutor = Executors.newSingleThreadExecutor(); + mExecutor = + Executors.newSingleThreadExecutor( + new NamedThreadFactory("AppFunctionSyncExecutors")); } /** diff --git a/services/appfunctions/java/com/android/server/appfunctions/NamedThreadFactory.java b/services/appfunctions/java/com/android/server/appfunctions/NamedThreadFactory.java new file mode 100644 index 000000000000..7adcc48a6a35 --- /dev/null +++ b/services/appfunctions/java/com/android/server/appfunctions/NamedThreadFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appfunctions; + +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** A {@link ThreadFactory} that creates threads with a given base name. */ +public class NamedThreadFactory implements ThreadFactory { + private final ThreadFactory mDefaultThreadFactory; + private final String mBaseName; + private final AtomicInteger mCount = new AtomicInteger(0); + + public NamedThreadFactory(final String baseName) { + mDefaultThreadFactory = Executors.defaultThreadFactory(); + mBaseName = baseName; + } + + @Override + public Thread newThread(Runnable runnable) { + final Thread thread = mDefaultThreadFactory.newThread(runnable); + thread.setName(mBaseName + "-" + mCount.getAndIncrement()); + return thread; + } +} diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig index ba8448548365..1803424ae7d7 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -49,3 +49,10 @@ flag { description: "Include the session id into the FillEventHistory events as part of ClientState" bug: "333927465" } + +flag { + name: "highlight_autofill_single_field" + namespace: "autofill" + description: "Highlight single field after autofill selection" + bug: "41496744" +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 2fa0e0d0d946..8f12b1db8f29 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -39,6 +39,7 @@ import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; +import static android.service.autofill.Flags.highlightAutofillSingleField; import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED; import static android.view.autofill.AutofillManager.ACTION_START_SESSION; import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED; @@ -49,7 +50,6 @@ import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN; import static android.view.autofill.AutofillManager.EXTRA_AUTOFILL_REQUEST_ID; import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM; import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString; - import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_EXPLICITLY_REQUESTED; import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_NORMAL_TRIGGER; @@ -104,7 +104,6 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Activity; import android.app.ActivityTaskManager; import android.app.IAssistDataReceiver; import android.app.PendingIntent; @@ -143,7 +142,6 @@ import android.os.TransactionTooLargeException; import android.service.assist.classification.FieldClassificationRequest; import android.service.assist.classification.FieldClassificationResponse; import android.service.autofill.AutofillFieldClassificationService.Scores; -import android.service.autofill.AutofillService; import android.service.autofill.CompositeUserData; import android.service.autofill.ConvertCredentialResponse; import android.service.autofill.Dataset; @@ -186,7 +184,6 @@ import android.view.autofill.IAutoFillManagerClient; import android.view.autofill.IAutofillWindowPresenter; import android.view.inputmethod.InlineSuggestionsRequest; import android.widget.RemoteViews; - import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; @@ -198,7 +195,6 @@ import com.android.server.autofill.ui.InlineFillUi; import com.android.server.autofill.ui.PendingUi; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; - import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -222,18 +218,21 @@ import java.util.function.Function; /** * A session for a given activity. * - * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track - * of the current {@link ViewState} to display the appropriate UI. + * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track of + * the current {@link ViewState} to display the appropriate UI. * - * <p>Although the autofill requests and callbacks are stateless from the service's point of - * view, we need to keep state in the framework side for cases such as authentication. For - * example, when service return a {@link FillResponse} that contains all the fields needed - * to fill the activity but it requires authentication first, that response need to be held - * until the user authenticates or it times out. + * <p>Although the autofill requests and callbacks are stateless from the service's point of view, + * we need to keep state in the framework side for cases such as authentication. For example, when + * service return a {@link FillResponse} that contains all the fields needed to fill the activity + * but it requires authentication first, that response need to be held until the user authenticates + * or it times out. */ -final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener, - AutoFillUI.AutoFillUiCallback, ValueFinder, - RemoteFieldClassificationService.FieldClassificationServiceCallbacks { +final class Session + implements RemoteFillService.FillServiceCallbacks, + ViewState.Listener, + AutoFillUI.AutoFillUiCallback, + ValueFinder, + RemoteFieldClassificationService.FieldClassificationServiceCallbacks { private static final String TAG = "AutofillSession"; // This should never be true in production. This is only for local debugging. @@ -257,6 +256,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private final AutofillManagerServiceImpl mService; private final Handler mHandler; private final AutoFillUI mUi; + /** * Context associated with the session, it has the same {@link Context#getDisplayId() displayId} * of the activity being autofilled. @@ -286,14 +286,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** Session is destroyed and removed from the manager service. */ public static final int STATE_REMOVED = 3; - @IntDef(prefix = { "STATE_" }, value = { - STATE_UNKNOWN, - STATE_ACTIVE, - STATE_FINISHED, - STATE_REMOVED - }) + @IntDef( + prefix = {"STATE_"}, + value = {STATE_UNKNOWN, STATE_ACTIVE, STATE_FINISHED, STATE_REMOVED}) @Retention(RetentionPolicy.SOURCE) - @interface SessionState{} + @interface SessionState {} @GuardedBy("mLock") private final SessionFlags mSessionFlags; @@ -318,7 +315,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public final int mFlags; @GuardedBy("mLock") - @NonNull private IBinder mActivityToken; + @NonNull + private IBinder mActivityToken; /** The app activity that's being autofilled */ @NonNull private final ComponentName mComponentName; @@ -341,11 +339,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * autofill. */ @GuardedBy("mLock") - @Nullable private Pair<Integer, InlineSuggestionsRequest> mLastInlineSuggestionsRequest; + @Nullable + private Pair<Integer, InlineSuggestionsRequest> mLastInlineSuggestionsRequest; - /** - * Id of the View currently being displayed. - */ + /** Id of the View currently being displayed. */ @GuardedBy("mLock") private @Nullable AutofillId mCurrentViewId; @@ -363,8 +360,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * * <p>Only {@code null} when the session is for augmented autofill only. */ - @Nullable - private final RemoteFillService mRemoteFillService; + @Nullable private final RemoteFillService mRemoteFillService; /** * With the credman integration, Autofill Framework handles two types of autofill flows - @@ -379,8 +375,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * service the session was initially created with, the secondary provider handler will contain * the remaining autofill service. */ - @Nullable - private final SecondaryProviderHandler mSecondaryProviderHandler; + @Nullable private final SecondaryProviderHandler mSecondaryProviderHandler; @GuardedBy("mLock") private SparseArray<FillResponse> mResponses; @@ -395,9 +390,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private ArrayList<FillContext> mContexts; - /** - * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. - */ + /** Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. */ private boolean mHasCallback; /** Whether the session has credential manager provider as the primary provider. */ @@ -426,32 +419,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private PendingUi mPendingSaveUi; - /** - * List of dataset ids selected by the user. - */ + /** List of dataset ids selected by the user. */ @GuardedBy("mLock") private ArrayList<String> mSelectedDatasetIds; - /** - * When the session started (using elapsed time since boot). - */ + /** When the session started (using elapsed time since boot). */ private final long mStartTime; - /** - * Count of FillRequests in the session. - */ + /** Count of FillRequests in the session. */ private int mRequestCount; /** - * Starting timestamp of latency logger. - * This is set when Session created or when the view is reset. + * Starting timestamp of latency logger. This is set when Session created or when the view is + * reset. */ @GuardedBy("mLock") private long mLatencyBaseTime; - /** - * When the UI was shown for the first time (using elapsed time since boot). - */ + /** When the UI was shown for the first time (using elapsed time since boot). */ @GuardedBy("mLock") private long mUiShownTime; @@ -475,15 +460,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private final LocalLog mWtfHistory; - /** - * Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id. - */ + /** Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id. */ @GuardedBy("mLock") private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1); - /** - * Destroys the augmented Autofill UI. - */ + /** Destroys the augmented Autofill UI. */ // TODO(b/123099468): this runnable is called when the Autofill session is destroyed, the // main reason being the cases where user tap HOME. // Right now it's completely destroying the UI, but we need to decide whether / how to @@ -494,13 +475,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Nullable private Runnable mAugmentedAutofillDestroyer; - /** - * List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics. - */ + /** List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics. */ @GuardedBy("mLock") private ArrayList<LogMaker> mAugmentedRequestsLogs; - /** * List of autofill ids of autofillable fields present in the AssistStructure that can be used * to trigger new augmented autofill requests (because the "standard" service was not interested @@ -509,23 +487,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private ArrayList<AutofillId> mAugmentedAutofillableIds; - @NonNull - final AutofillInlineSessionController mInlineSessionController; + @NonNull final AutofillInlineSessionController mInlineSessionController; - /** - * Receiver of assist data from the app's {@link Activity}. - */ + /** Receiver of assist data from the app's {@link Activity}. */ private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl(); - /** - * Receiver of assist data for pcc purpose - */ + /** Receiver of assist data for pcc purpose */ private final PccAssistDataReceiverImpl mPccAssistReceiver = new PccAssistDataReceiverImpl(); private final ClassificationState mClassificationState = new ClassificationState(); - @Nullable - private final ComponentName mCredentialAutofillService; + @Nullable private final ComponentName mCredentialAutofillService; // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a // new one per Session. @@ -547,7 +519,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.v(TAG, "mDelayedFillBroadcastReceiver delayed fill action received"); synchronized (mLock) { int requestId = intent.getIntExtra(EXTRA_REQUEST_ID, 0); - FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE, android.service.autofill.FillResponse.class); + FillResponse response = + intent.getParcelableExtra( + EXTRA_FILL_RESPONSE, + android.service.autofill.FillResponse.class); mFillRequestEventLogger.maybeSetRequestTriggerReason( TRIGGER_REASON_RETRIGGER); mAssistReceiver.processDelayedFillLocked(requestId, response); @@ -575,28 +550,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private SessionCommittedEventLogger mSessionCommittedEventLogger; - /** - * Fill dialog request would likely be sent slightly later. - */ + /** Fill dialog request would likely be sent slightly later. */ @NonNull @GuardedBy("mLock") private boolean mPreviouslyFillDialogPotentiallyStarted; /** - * Keeps track of if the user entered view, this is used to - * distinguish Fill Request that did not have user interaction - * with ones that did. + * Keeps track of if the user entered view, this is used to distinguish Fill Request that did + * not have user interaction with ones that did. * - * This is set to true when entering view - after FillDialog FillRequest - * or on plain user tap. + * <p>This is set to true when entering view - after FillDialog FillRequest or on plain user + * tap. */ @NonNull @GuardedBy("mLock") private boolean mLogViewEntered; /** - * Keeps the fill dialog trigger ids of the last response. This invalidates - * the trigger ids of the previous response. + * Keeps the fill dialog trigger ids of the last response. This invalidates the trigger ids of + * the previous response. */ @Nullable @GuardedBy("mLock") @@ -662,9 +634,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } - /** - * Collection of flags/booleans that helps determine Session behaviors. - */ + /** Collection of flags/booleans that helps determine Session behaviors. */ private final class SessionFlags { /** Whether autofill is disabled by the service */ private boolean mAutofillDisabled; @@ -695,31 +665,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub { @GuardedBy("mLock") private boolean mWaitForInlineRequest; + @GuardedBy("mLock") private InlineSuggestionsRequest mPendingInlineSuggestionsRequest; + @GuardedBy("mLock") private FillRequest mPendingFillRequest; + @GuardedBy("mLock") private FillRequest mLastFillRequest; - @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState, - boolean isInlineRequest) { + @Nullable + Consumer<InlineSuggestionsRequest> newAutofillRequestLocked( + ViewState viewState, boolean isInlineRequest) { mPendingFillRequest = null; mWaitForInlineRequest = isInlineRequest; mPendingInlineSuggestionsRequest = null; if (isInlineRequest) { WeakReference<AssistDataReceiverImpl> assistDataReceiverWeakReference = - new WeakReference<AssistDataReceiverImpl>(this); + new WeakReference<AssistDataReceiverImpl>(this); WeakReference<ViewState> viewStateWeakReference = - new WeakReference<ViewState>(viewState); - return new InlineSuggestionRequestConsumer(assistDataReceiverWeakReference, - viewStateWeakReference); + new WeakReference<ViewState>(viewState); + return new InlineSuggestionRequestConsumer( + assistDataReceiverWeakReference, viewStateWeakReference); } return null; } - void handleInlineSuggestionRequest(InlineSuggestionsRequest inlineSuggestionsRequest, - ViewState viewState) { + void handleInlineSuggestionRequest( + InlineSuggestionsRequest inlineSuggestionsRequest, ViewState viewState) { if (sVerbose) { Slog.v(TAG, "handleInlineSuggestionRequest(): inline suggestion request received"); } @@ -738,8 +712,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void maybeRequestFillLocked() { if (mPendingFillRequest == null) { if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request " - + "due to empty pending fill request"); + Slog.v( + TAG, + "maybeRequestFillLocked(): cancelling calling fill request " + + "due to empty pending fill request"); } return; } @@ -748,27 +724,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mWaitForInlineRequest) { if (mPendingInlineSuggestionsRequest == null) { if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request " - + "due to waiting for inline request and pending inline request is " - + "currently empty"); + Slog.v( + TAG, + "maybeRequestFillLocked(): cancelling calling fill request due to" + + " waiting for inline request and pending inline request is" + + " currently empty"); } return; } if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): adding inline request to pending " - + "fill request"); + Slog.v( + TAG, + "maybeRequestFillLocked(): adding inline request to pending " + + "fill request"); } - mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), - mPendingFillRequest.getFillContexts(), - mPendingFillRequest.getHints(), - mPendingFillRequest.getClientState(), - mPendingFillRequest.getFlags(), - mPendingInlineSuggestionsRequest, - mPendingFillRequest.getDelayedFillIntentSender()); + mPendingFillRequest = + new FillRequest( + mPendingFillRequest.getId(), + mPendingFillRequest.getFillContexts(), + mPendingFillRequest.getHints(), + mPendingFillRequest.getClientState(), + mPendingFillRequest.getFlags(), + mPendingInlineSuggestionsRequest, + mPendingFillRequest.getDelayedFillIntentSender()); } else { if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): not adding inline request to pending " - + "fill request"); + Slog.v( + TAG, + "maybeRequestFillLocked(): not adding inline request to pending " + + "fill request"); } } @@ -780,19 +764,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState && mSecondaryProviderHandler != null) { Slog.v(TAG, "Requesting fill response to secondary provider."); if (!mIsPrimaryCredential) { - mPendingFillRequest = addCredentialManagerDataToClientState( - mPendingFillRequest, - mPendingInlineSuggestionsRequest, id); + mPendingFillRequest = + addCredentialManagerDataToClientState( + mPendingFillRequest, mPendingInlineSuggestionsRequest, id); } - mSecondaryProviderHandler.onFillRequest(mPendingFillRequest, - mPendingFillRequest.getFlags(), mClient.asBinder()); + mSecondaryProviderHandler.onFillRequest( + mPendingFillRequest, mPendingFillRequest.getFlags(), mClient.asBinder()); } else if (mRemoteFillService != null) { if (mIsPrimaryCredential) { - mPendingFillRequest = addCredentialManagerDataToClientState( - mPendingFillRequest, - mPendingInlineSuggestionsRequest, id); - mRemoteFillService.onFillCredentialRequest(mPendingFillRequest, - mClient.asBinder()); + mPendingFillRequest = + addCredentialManagerDataToClientState( + mPendingFillRequest, mPendingInlineSuggestionsRequest, id); + mRemoteFillService.onFillCredentialRequest( + mPendingFillRequest, mClient.asBinder()); } else { mRemoteFillService.onFillRequest(mPendingFillRequest); } @@ -813,8 +797,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void onHandleAssistData(Bundle resultData) throws RemoteException { if (mRemoteFillService == null) { - wtf(null, "onHandleAssistData() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); + wtf( + null, + "onHandleAssistData() called without a remote service. " + + "mForAugmentedAutofillOnly: %s", + mSessionFlags.mAugmentedAutofillOnly); return; } // Keeps to prevent it is cleared on multiple threads. @@ -824,7 +811,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class); + final AssistStructure structure = + resultData.getParcelable( + ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class); if (structure == null) { Slog.e(TAG, "No assist structure - app might have crashed providing it"); return; @@ -852,13 +841,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { structure.ensureDataForAutofill(); } catch (RuntimeException e) { - wtf(e, "Exception lazy loading assist structure for %s: %s", - structure.getActivityComponent(), e); + wtf( + e, + "Exception lazy loading assist structure for %s: %s", + structure.getActivityComponent(), + e); return; } - final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure, - /* autofillableOnly= */false); + final ArrayList<AutofillId> ids = + Helper.getAutofillIds(structure, /* autofillableOnly= */ false); for (int i = 0; i < ids.size(); i++) { ids.get(i).setSessionId(Session.this.id); } @@ -868,8 +860,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mCompatMode) { // Sanitize URL bar, if needed - final String[] urlBarIds = mService.getUrlBarResourceIdsForCompatMode( - mComponentName.getPackageName()); + final String[] urlBarIds = + mService.getUrlBarResourceIdsForCompatMode( + mComponentName.getPackageName()); if (sDebug) { Slog.d(TAG, "url_bars in compat mode: " + Arrays.toString(urlBarIds)); } @@ -878,11 +871,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mUrlBar != null) { final AutofillId urlBarId = mUrlBar.getAutofillId(); if (sDebug) { - Slog.d(TAG, "Setting urlBar as id=" + urlBarId + " and domain " - + mUrlBar.getWebDomain()); + Slog.d( + TAG, + "Setting urlBar as id=" + + urlBarId + + " and domain " + + mUrlBar.getWebDomain()); } - final ViewState viewState = new ViewState(urlBarId, Session.this, - ViewState.STATE_URL_BAR, mIsPrimaryCredential); + final ViewState viewState = + new ViewState( + urlBarId, + Session.this, + ViewState.STATE_URL_BAR, + mIsPrimaryCredential); mViewStates.put(urlBarId, viewState); } } @@ -907,11 +908,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final List<String> hints = getTypeHintsForProvider(); mDelayedFillPendingIntent = createPendingIntent(requestId); - request = new FillRequest(requestId, contexts, hints, mClientState, flags, - /*inlineSuggestionsRequest=*/ null, - /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null - ? null - : mDelayedFillPendingIntent.getIntentSender()); + request = + new FillRequest( + requestId, + contexts, + hints, + mClientState, + flags, + /* inlineSuggestionsRequest= */ null, + /* delayedFillIntentSender= */ mDelayedFillPendingIntent == null + ? null + : mDelayedFillPendingIntent.getIntentSender()); mPendingFillRequest = request; maybeRequestFillLocked(); @@ -930,39 +937,49 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void processDelayedFillLocked(int requestId, FillResponse response) { if (mLastFillRequest != null && requestId == mLastFillRequest.getId()) { - Slog.v(TAG, "processDelayedFillLocked: " - + "calling onFillRequestSuccess with new response"); - onFillRequestSuccess(requestId, response, - mService.getServicePackageName(), mLastFillRequest.getFlags()); + Slog.v( + TAG, + "processDelayedFillLocked: " + + "calling onFillRequestSuccess with new response"); + onFillRequestSuccess( + requestId, + response, + mService.getServicePackageName(), + mLastFillRequest.getFlags()); } } } - private FillRequest addCredentialManagerDataToClientState(FillRequest pendingFillRequest, - InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) { + private FillRequest addCredentialManagerDataToClientState( + FillRequest pendingFillRequest, + InlineSuggestionsRequest pendingInlineSuggestionsRequest, + int sessionId) { if (pendingFillRequest.getClientState() == null) { - pendingFillRequest = new FillRequest(pendingFillRequest.getId(), - pendingFillRequest.getFillContexts(), - pendingFillRequest.getHints(), - new Bundle(), - pendingFillRequest.getFlags(), - pendingInlineSuggestionsRequest, - pendingFillRequest.getDelayedFillIntentSender()); + pendingFillRequest = + new FillRequest( + pendingFillRequest.getId(), + pendingFillRequest.getFillContexts(), + pendingFillRequest.getHints(), + new Bundle(), + pendingFillRequest.getFlags(), + pendingInlineSuggestionsRequest, + pendingFillRequest.getDelayedFillIntentSender()); } pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId); pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId()); - ResultReceiver resultReceiver = constructCredentialManagerCallback( - pendingFillRequest.getId()); - pendingFillRequest.getClientState().putParcelable( - CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver); + ResultReceiver resultReceiver = + constructCredentialManagerCallback(pendingFillRequest.getId()); + pendingFillRequest + .getClientState() + .putParcelable(CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver); return pendingFillRequest; } /** - * Get the list of valid autofill hint types from Device flags - * Returns empty list if PCC is off or no types available - */ + * Get the list of valid autofill hint types from Device flags Returns empty list if PCC is off + * or no types available + */ private List<String> getTypeHintsForProvider() { if (!mService.isPccClassificationEnabled()) { return Collections.EMPTY_LIST; @@ -978,9 +995,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return List.of(typeHints.split(PCC_HINTS_DELIMITER)); } - /** - * Assist Data Receiver for PCC - */ + /** Assist Data Receiver for PCC */ private final class PccAssistDataReceiverImpl extends IAssistDataReceiver.Stub { @GuardedBy("mLock") @@ -998,7 +1013,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState new WeakReference<>(Session.this); remoteFieldClassificationService.onFieldClassificationRequest( mClassificationState.mPendingFieldClassificationRequest, - fieldClassificationServiceCallbacksWeakRef); + fieldClassificationServiceCallbacksWeakRef); } mClassificationState.onFieldClassificationRequestSent(); } @@ -1006,26 +1021,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void onHandleAssistData(Bundle resultData) throws RemoteException { // TODO: add a check if pcc field classification service is present - final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE, - android.app.assist.AssistStructure.class); + final AssistStructure structure = + resultData.getParcelable( + ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class); if (structure == null) { - Slog.e(TAG, "No assist structure for pcc detection - " - + "app might have crashed providing it"); + Slog.e( + TAG, + "No assist structure for pcc detection - " + + "app might have crashed providing it"); return; } final Bundle receiverExtras = resultData.getBundle(ASSIST_KEY_RECEIVER_EXTRAS); if (receiverExtras == null) { - Slog.e(TAG, "No receiver extras for pcc detection - " - + "app might have crashed providing it"); + Slog.e( + TAG, + "No receiver extras for pcc detection - " + + "app might have crashed providing it"); return; } final int requestId = receiverExtras.getInt(EXTRA_REQUEST_ID); if (sVerbose) { - Slog.v(TAG, "New structure for PCC Detection: requestId " + requestId + ": " - + structure); + Slog.v( + TAG, + "New structure for PCC Detection: requestId " + + requestId + + ": " + + structure); } synchronized (mLock) { @@ -1037,13 +1061,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { structure.ensureDataForAutofill(); } catch (RuntimeException e) { - wtf(e, "Exception lazy loading assist structure for %s: %s", - structure.getActivityComponent(), e); + wtf( + e, + "Exception lazy loading assist structure for %s: %s", + structure.getActivityComponent(), + e); return; } - final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure, - /* autofillableOnly= */false); + final ArrayList<AutofillId> ids = + Helper.getAutofillIds(structure, /* autofillableOnly= */ false); for (int i = 0; i < ids.size(); i++) { ids.get(i).setSessionId(Session.this.id); } @@ -1066,13 +1093,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState PendingIntent pendingIntent; final long identity = Binder.clearCallingIdentity(); try { - Intent intent = new Intent(ACTION_DELAYED_FILL).setPackage("android") - .putExtra(EXTRA_REQUEST_ID, requestId); - pendingIntent = PendingIntent.getBroadcast( - mContext, this.id, intent, - PendingIntent.FLAG_MUTABLE - | PendingIntent.FLAG_ONE_SHOT - | PendingIntent.FLAG_CANCEL_CURRENT); + Intent intent = + new Intent(ACTION_DELAYED_FILL) + .setPackage("android") + .putExtra(EXTRA_REQUEST_ID, requestId); + pendingIntent = + PendingIntent.getBroadcast( + mContext, + this.id, + intent, + PendingIntent.FLAG_MUTABLE + | PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_CANCEL_CURRENT); } finally { Binder.restoreCallingIdentity(identity); } @@ -1113,9 +1145,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Returns the ids of all entries in {@link #mViewStates} in the same order. - */ + /** Returns the ids of all entries in {@link #mViewStates} in the same order. */ @GuardedBy("mLock") private AutofillId[] getIdsOfAllViewStatesLocked() { final int numViewState = mViewStates.size(); @@ -1164,8 +1194,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * <p>Gets the value of a field, using either the {@code viewStates} or the {@code mContexts}, - * or {@code null} when not found on either of them. + * Gets the value of a field, using either the {@code viewStates} or the {@code mContexts}, or + * {@code null} when not found on either of them. */ @GuardedBy("mLock") @Nullable @@ -1180,16 +1210,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<Session> previousSessions = mService.getPreviousSessionsLocked(this); if (previousSessions != null) { if (sDebug) { - Slog.d(TAG, "findValueLocked(): looking on " + previousSessions.size() - + " previous sessions for autofillId " + autofillId); + Slog.d( + TAG, + "findValueLocked(): looking on " + + previousSessions.size() + + " previous sessions for autofillId " + + autofillId); } for (int i = 0; i < previousSessions.size(); i++) { final Session previousSession = previousSessions.get(i); - final AutofillValue previousValue = previousSession - .findValueFromThisSessionOnlyLocked(autofillId); + final AutofillValue previousValue = + previousSession.findValueFromThisSessionOnlyLocked(autofillId); if (previousValue != null) { - return getSanitizedValue(createSanitizers(previousSession.getSaveInfoLocked()), - autofillId, previousValue); + return getSanitizedValue( + createSanitizers(previousSession.getSaveInfoLocked()), + autofillId, + previousValue); } } } @@ -1212,16 +1248,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState AutofillValue candidateSaveValue = state.getCandidateSaveValue(); if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { if (sDebug) { - Slog.d(TAG, "findValueLocked(): current value for " + autofillId - + " is empty, using candidateSaveValue instead."); + Slog.d( + TAG, + "findValueLocked(): current value for " + + autofillId + + " is empty, using candidateSaveValue instead."); } return candidateSaveValue; } } if (value == null) { if (sDebug) { - Slog.d(TAG, "findValueLocked(): no current value for " + autofillId - + ", checking value from previous fill contexts"); + Slog.d( + TAG, + "findValueLocked(): no current value for " + + autofillId + + ", checking value from previous fill contexts"); value = getValueFromContextsLocked(autofillId); } } @@ -1231,17 +1273,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Updates values of the nodes in the context's structure so that: * - * - proper node is focused - * - autofillValue is sent back to service when it was previously autofilled - * - autofillValue is sent in the view used to force a request + * <p>- proper node is focused - autofillValue is sent back to service when it was previously + * autofilled - autofillValue is sent in the view used to force a request * * @param fillContext The context to be filled * @param flags The flags that started the session */ @GuardedBy("mLock") private void fillContextWithAllowedValuesLocked(@NonNull FillContext fillContext, int flags) { - final ViewNode[] nodes = fillContext - .findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); + final ViewNode[] nodes = + fillContext.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); final int numViewState = mViewStates.size(); for (int i = 0; i < numViewState; i++) { @@ -1250,7 +1291,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ViewNode node = nodes[i]; if (node == null) { if (sVerbose) { - Slog.v(TAG, + Slog.v( + TAG, "fillContextWithAllowedValuesLocked(): no node for " + viewState.id); } continue; @@ -1277,14 +1319,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Cancels the last request sent to the {@link #mRemoteFillService}. - */ + /** Cancels the last request sent to the {@link #mRemoteFillService}. */ @GuardedBy("mLock") private void cancelCurrentRequestLocked() { if (mRemoteFillService == null) { - wtf(null, "cancelCurrentRequestLocked() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); + wtf( + null, + "cancelCurrentRequestLocked() called without a remote service. " + + "mForAugmentedAutofillOnly: %s", + mSessionFlags.mAugmentedAutofillOnly); return; } final int canceledRequest = mRemoteFillService.cancelCurrentRequest(); @@ -1318,21 +1361,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private Optional<Integer> requestNewFillResponseLocked( @NonNull ViewState viewState, int newState, int flags) { boolean isSecondary = shouldRequestSecondaryProvider(flags); - final FillResponse existingResponse = isSecondary - ? viewState.getSecondaryResponse() : viewState.getResponse(); + final FillResponse existingResponse = + isSecondary ? viewState.getSecondaryResponse() : viewState.getResponse(); mFillRequestEventLogger.startLogForNewRequest(); mRequestCount++; mFillRequestEventLogger.maybeSetAppPackageUid(uid); mFillRequestEventLogger.maybeSetFlags(mFlags); - if(mPreviouslyFillDialogPotentiallyStarted) { + if (mPreviouslyFillDialogPotentiallyStarted) { mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_PRE_TRIGGER); } else { if ((flags & FLAG_MANUAL_REQUEST) != 0) { mFillRequestEventLogger.maybeSetRequestTriggerReason( TRIGGER_REASON_EXPLICITLY_REQUESTED); } else { - mFillRequestEventLogger.maybeSetRequestTriggerReason( - TRIGGER_REASON_NORMAL_TRIGGER); + mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_NORMAL_TRIGGER); } } if (existingResponse != null) { @@ -1348,9 +1390,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionState = STATE_ACTIVE; if (mSessionFlags.mAugmentedAutofillOnly || mRemoteFillService == null) { if (sVerbose) { - Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead " - + "(mForAugmentedAutofillOnly=" + mSessionFlags.mAugmentedAutofillOnly - + ", flags=" + flags + ")"); + Slog.v( + TAG, + "requestNewFillResponse(): triggering augmented autofill instead " + + "(mForAugmentedAutofillOnly=" + + mSessionFlags.mAugmentedAutofillOnly + + ", flags=" + + flags + + ")"); } mSessionFlags.mAugmentedAutofillOnly = true; mFillRequestEventLogger.maybeSetRequestId(AUGMENTED_AUTOFILL_REQUEST_ID); @@ -1365,16 +1412,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Create a metrics log for the request final int ordinal = mRequestLogs.size() + 1; - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL, ordinal); + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_REQUEST) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL, ordinal); if (flags != 0) { log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags); } mRequestLogs.put(requestId, log); if (sVerbose) { - Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId - + ", flags=" + flags); + Slog.v( + TAG, + "Requesting structure for request #" + + ordinal + + " ,requestId=" + + requestId + + ", flags=" + + flags); } boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0; mFillRequestEventLogger.maybeSetRequestId(requestId); @@ -1406,11 +1460,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // is also not focused. final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); - if (mSessionFlags.mInlineSupportedByService && remoteRenderService != null - && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) { + if (mSessionFlags.mInlineSupportedByService + && remoteRenderService != null + && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) { Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = - mAssistReceiver.newAutofillRequestLocked(viewState, - /* isInlineRequest= */ true); + mAssistReceiver.newAutofillRequestLocked( + viewState, /* isInlineRequest= */ true); if (inlineSuggestionsRequestConsumer != null) { final int requestIdCopy = requestId; final AutofillId focusedId = mCurrentViewId; @@ -1423,8 +1478,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState requestIdCopy, inlineSuggestionsRequestConsumer, focusedId); - RemoteCallback inlineSuggestionRendorInfoCallback = new RemoteCallback( - inlineSuggestionRendorInfoCallbackOnResultListener, mHandler); + RemoteCallback inlineSuggestionRendorInfoCallback = + new RemoteCallback( + inlineSuggestionRendorInfoCallbackOnResultListener, mHandler); remoteRenderService.getInlineSuggestionsRendererInfo( inlineSuggestionRendorInfoCallback); @@ -1465,8 +1521,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState receiverExtras.putInt(EXTRA_REQUEST_ID, requestId); final long identity = Binder.clearCallingIdentity(); try { - if (!ActivityTaskManager.getService().requestAutofillData(mPccAssistReceiver, - receiverExtras, mActivityToken, flags)) { + if (!ActivityTaskManager.getService() + .requestAutofillData( + mPccAssistReceiver, receiverExtras, mActivityToken, flags)) { Slog.w(TAG, "failed to request autofill data for " + mActivityToken); } } finally { @@ -1483,8 +1540,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState receiverExtras.putInt(EXTRA_REQUEST_ID, requestId); final long identity = Binder.clearCallingIdentity(); try { - if (!ActivityTaskManager.getService().requestAutofillData(mAssistReceiver, - receiverExtras, mActivityToken, flags)) { + if (!ActivityTaskManager.getService() + .requestAutofillData( + mAssistReceiver, receiverExtras, mActivityToken, flags)) { Slog.w(TAG, "failed to request autofill data for " + mActivityToken); } } finally { @@ -1495,13 +1553,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui, - @NonNull Context context, @NonNull Handler handler, int userId, @NonNull Object lock, - int sessionId, int taskId, int uid, @NonNull IBinder activityToken, - @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory, - @NonNull LocalLog wtfHistory, @Nullable ComponentName serviceComponentName, - @NonNull ComponentName componentName, boolean compatMode, - boolean bindInstantServiceAllowed, boolean forAugmentedAutofillOnly, int flags, + Session( + @NonNull AutofillManagerServiceImpl service, + @NonNull AutoFillUI ui, + @NonNull Context context, + @NonNull Handler handler, + int userId, + @NonNull Object lock, + int sessionId, + int taskId, + int uid, + @NonNull IBinder activityToken, + @NonNull IBinder client, + boolean hasCallback, + @NonNull LocalLog uiLatencyHistory, + @NonNull LocalLog wtfHistory, + @Nullable ComponentName serviceComponentName, + @NonNull ComponentName componentName, + boolean compatMode, + boolean bindInstantServiceAllowed, + boolean forAugmentedAutofillOnly, + int flags, @NonNull InputMethodManagerInternal inputMethodManagerInternal, boolean isPrimaryCredential) { if (sessionId < 0) { @@ -1533,22 +1605,40 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState primaryServiceComponentName = serviceComponentName; secondaryServiceComponentName = mCredentialAutofillService; } - Slog.v(TAG, "Primary service component name: " + primaryServiceComponentName - + ", secondary service component name: " + secondaryServiceComponentName); - - mRemoteFillService = primaryServiceComponentName == null ? null - : new RemoteFillService(context, primaryServiceComponentName, userId, this, - bindInstantServiceAllowed, mCredentialAutofillService); - mSecondaryProviderHandler = secondaryServiceComponentName == null ? null - : new SecondaryProviderHandler(context, userId, bindInstantServiceAllowed, - this::onSecondaryFillResponse, secondaryServiceComponentName, - mCredentialAutofillService); + Slog.v( + TAG, + "Primary service component name: " + + primaryServiceComponentName + + ", secondary service component name: " + + secondaryServiceComponentName); + + mRemoteFillService = + primaryServiceComponentName == null + ? null + : new RemoteFillService( + context, + primaryServiceComponentName, + userId, + this, + bindInstantServiceAllowed, + mCredentialAutofillService); + mSecondaryProviderHandler = + secondaryServiceComponentName == null + ? null + : new SecondaryProviderHandler( + context, + userId, + bindInstantServiceAllowed, + this::onSecondaryFillResponse, + secondaryServiceComponentName, + mCredentialAutofillService); mActivityToken = activityToken; mHasCallback = hasCallback; mUiLatencyHistory = uiLatencyHistory; mWtfHistory = wtfHistory; - int displayId = LocalServices.getService(ActivityTaskManagerInternal.class) - .getDisplayId(activityToken); + int displayId = + LocalServices.getService(ActivityTaskManagerInternal.class) + .getDisplayId(activityToken); mContext = Helper.getDisplayContext(context, displayId); mComponentName = componentName; mCompatMode = compatMode; @@ -1557,8 +1647,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mStartTime = SystemClock.elapsedRealtime(); mLatencyBaseTime = mStartTime; mRequestCount = 0; - mPresentationStatsEventLogger = PresentationStatsEventLogger.createPresentationLog( - sessionId, uid, mLatencyBaseTime); + mPresentationStatsEventLogger = + PresentationStatsEventLogger.createPresentationLog( + sessionId, uid, mLatencyBaseTime); mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId); mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId); mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId); @@ -1574,33 +1665,39 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState setClientLocked(client); } - mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal, - userId, componentName, handler, mLock, - new InlineFillUi.InlineUiEventCallback() { - @Override - public void notifyInlineUiShown(AutofillId autofillId) { - notifyFillUiShown(autofillId); - } + mInlineSessionController = + new AutofillInlineSessionController( + inputMethodManagerInternal, + userId, + componentName, + handler, + mLock, + new InlineFillUi.InlineUiEventCallback() { + @Override + public void notifyInlineUiShown(AutofillId autofillId) { + notifyFillUiShown(autofillId); + } - @Override - public void notifyInlineUiHidden(AutofillId autofillId) { - notifyFillUiHidden(autofillId); - } - }); + @Override + public void notifyInlineUiHidden(AutofillId autofillId) { + notifyFillUiHidden(autofillId); + } + }); - mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags)); + mMetricsLogger.write( + newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags)); mLogViewEntered = false; } private ComponentName getCredentialAutofillService(Context context) { ComponentName componentName = null; - String credentialManagerAutofillCompName = context.getResources().getString( - R.string.config_defaultCredentialManagerAutofillService); + String credentialManagerAutofillCompName = + context.getResources() + .getString(R.string.config_defaultCredentialManagerAutofillService); if (credentialManagerAutofillCompName != null && !credentialManagerAutofillCompName.isEmpty()) { - componentName = ComponentName.unflattenFromString( - credentialManagerAutofillCompName); + componentName = ComponentName.unflattenFromString(credentialManagerAutofillCompName); } if (componentName == null) { Slog.w(TAG, "Invalid CredentialAutofillService"); @@ -1614,7 +1711,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * @return The activity token */ @GuardedBy("mLock") - @NonNull IBinder getActivityTokenLocked() { + @NonNull + IBinder getActivityTokenLocked() { return mActivityToken; } @@ -1627,8 +1725,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void switchActivity(@NonNull IBinder newActivity, @NonNull IBinder newClient) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#switchActivity() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#switchActivity() rejected - session: " + + id + + " destroyed"); return; } mActivityToken = newActivity; @@ -1643,17 +1744,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private void setClientLocked(@NonNull IBinder client) { unlinkClientVultureLocked(); mClient = IAutoFillManagerClient.Stub.asInterface(client); - mClientVulture = () -> { - synchronized (mLock) { - Slog.d(TAG, "handling death of " + mActivityToken + " when saving=" - + mSessionFlags.mShowingSaveUi); - if (mSessionFlags.mShowingSaveUi) { - mUi.hideFillUi(this); - } else { - mUi.destroyAll(mPendingSaveUi, this, false); - } - } - }; + mClientVulture = + () -> { + synchronized (mLock) { + Slog.d( + TAG, + "handling death of " + + mActivityToken + + " when saving=" + + mSessionFlags.mShowingSaveUi); + if (mSessionFlags.mShowingSaveUi) { + mUi.hideFillUi(this); + } else { + mUi.destroyAll(mPendingSaveUi, this, false); + } + } + }; try { mClient.asBinder().linkToDeath(mClientVulture, 0); } catch (RemoteException e) { @@ -1676,8 +1782,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override @SuppressWarnings("GuardedBy") - public void onFillRequestSuccess(int requestId, @Nullable FillResponse response, - @NonNull String servicePackageName, int requestFlags) { + public void onFillRequestSuccess( + int requestId, + @Nullable FillResponse response, + @NonNull String servicePackageName, + int requestFlags) { final AutofillId[] fieldClassificationIds; final LogMaker requestLog; @@ -1701,8 +1810,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getDetectionPreferenceForLogging()); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onFillRequestSuccess() rejected - session: " + + id + + " destroyed"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; @@ -1713,8 +1825,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // saveUi gets closed, the session will be destroyed and AutofillManager will reset // its state. Processing the fill request will result in a great chance of corrupt // state in Autofill. - Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: " - + id + " is showing saveUi"); + Slog.w( + TAG, + "Call to Session#onFillRequestSuccess() rejected - session: " + + id + + " is showing saveUi"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; @@ -1760,22 +1875,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - final long disableDuration = response.getDisableDuration(); final boolean autofillDisabled = disableDuration > 0; if (autofillDisabled) { final int flags = response.getFlags(); final boolean disableActivityOnly = (flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0; - notifyDisableAutofillToClient(disableDuration, - disableActivityOnly ? mComponentName : null); + notifyDisableAutofillToClient( + disableDuration, disableActivityOnly ? mComponentName : null); if (disableActivityOnly) { - mService.disableAutofillForActivity(mComponentName, disableDuration, - id, mCompatMode); + mService.disableAutofillForActivity( + mComponentName, disableDuration, id, mCompatMode); } else { - mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration, - id, mCompatMode); + mService.disableAutofillForApp( + mComponentName.getPackageName(), disableDuration, id, mCompatMode); } synchronized (mLock) { @@ -1786,17 +1900,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (triggerAugmentedAutofillLocked(requestFlags) != null) { mSessionFlags.mAugmentedAutofillOnly = true; if (sDebug) { - Slog.d(TAG, "Service disabled autofill for " + mComponentName - + ", but session is kept for augmented autofill only"); + Slog.d( + TAG, + "Service disabled autofill for " + + mComponentName + + ", but session is kept for augmented autofill only"); } return; } } if (sDebug) { - final StringBuilder message = new StringBuilder("Service disabled autofill for ") + final StringBuilder message = + new StringBuilder("Service disabled autofill for ") .append(mComponentName) - .append(": flags=").append(flags) + .append(": flags=") + .append(flags) .append(", duration="); TimeUtils.formatDuration(disableDuration, message); Slog.d(TAG, message.toString()); @@ -1816,8 +1935,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (requestLog != null) { - requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, - response.getDatasets() == null ? 0 : response.getDatasets().size()); + requestLog.addTaggedData( + MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, + response.getDatasets() == null ? 0 : response.getDatasets().size()); if (fieldClassificationIds != null) { requestLog.addTaggedData( MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS, @@ -1840,10 +1960,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - @GuardedBy("mLock") - private void processResponseLockedForPcc(@NonNull FillResponse response, - @Nullable Bundle newClientState, int flags) { + private void processResponseLockedForPcc( + @NonNull FillResponse response, @Nullable Bundle newClientState, int flags) { if (DBG) { Slog.d(TAG, "DBG: Initial response: " + response); } @@ -1851,9 +1970,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState response = getEffectiveFillResponse(response); if (isEmptyResponse(response)) { // Treat it as a null response. - processNullResponseLocked( - response != null ? response.getRequestId() : 0, - flags); + processNullResponseLocked(response != null ? response.getRequestId() : 0, flags); return; } if (DBG) { @@ -1870,9 +1987,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return ((response.getDatasets() == null || response.getDatasets().isEmpty()) && response.getAuthentication() == null && (saveInfo == null - || (ArrayUtils.isEmpty(saveInfo.getOptionalIds()) - && ArrayUtils.isEmpty(saveInfo.getRequiredIds()) - && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0))) + || (ArrayUtils.isEmpty(saveInfo.getOptionalIds()) + && ArrayUtils.isEmpty(saveInfo.getRequiredIds()) + && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0))) && (ArrayUtils.isEmpty(response.getFieldClassificationIds()))); } } @@ -1884,10 +2001,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState computeDatasetsForProviderAndUpdateContainer(response, autofillProviderContainer); if (DBG) { - Slog.d(TAG, "DBG: computeDatasetsForProviderAndUpdateContainer: " - + autofillProviderContainer); + Slog.d( + TAG, + "DBG: computeDatasetsForProviderAndUpdateContainer: " + + autofillProviderContainer); } - if (!mService.isPccClassificationEnabled()) { + if (!mService.isPccClassificationEnabled()) { if (sVerbose) { Slog.v(TAG, "PCC classification is disabled"); } @@ -1897,10 +2016,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mClassificationState.mState != ClassificationState.STATE_RESPONSE || mClassificationState.mLastFieldClassificationResponse == null) { if (sVerbose) { - Slog.v(TAG, "PCC classification no last response:" - + (mClassificationState.mLastFieldClassificationResponse == null) - + " ,ineligible state=" - + (mClassificationState.mState != ClassificationState.STATE_RESPONSE)); + Slog.v( + TAG, + "PCC classification no last response:" + + (mClassificationState.mLastFieldClassificationResponse + == null) + + " ,ineligible state=" + + (mClassificationState.mState + != ClassificationState.STATE_RESPONSE)); } return createShallowCopy(response, autofillProviderContainer); } @@ -1966,8 +2089,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( (int) (fillRequestReceivedRelativeTimestamp)); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onSecondaryFillResponse() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onSecondaryFillResponse() rejected - session: " + + id + + " destroyed"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; @@ -1981,7 +2107,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSecondaryResponses = new SparseArray<>(2); } mSecondaryResponses.put(fillResponse.getRequestId(), fillResponse); - setViewStatesLocked(fillResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false, + setViewStatesLocked( + fillResponse, + ViewState.STATE_FILLABLE, + /* clearResponse= */ false, /* isPrimary= */ false); // Updates the UI, if necessary. @@ -1997,16 +2126,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private FillResponse createShallowCopy( FillResponse response, DatasetComputationContainer container) { return FillResponse.shallowCopy( - response, - new ArrayList<>(container.mDatasets), - getEligibleSaveInfo(response)); + response, new ArrayList<>(container.mDatasets), getEligibleSaveInfo(response)); } private SaveInfo getEligibleSaveInfo(FillResponse response) { SaveInfo saveInfo = response.getSaveInfo(); - if (saveInfo == null || (!ArrayUtils.isEmpty(saveInfo.getOptionalIds()) - || !ArrayUtils.isEmpty(saveInfo.getRequiredIds()) - || (saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0)) { + if (saveInfo == null + || (!ArrayUtils.isEmpty(saveInfo.getOptionalIds()) + || !ArrayUtils.isEmpty(saveInfo.getRequiredIds()) + || (saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0)) { return saveInfo; } synchronized (mLock) { @@ -2019,12 +2147,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState ArraySet<AutofillId> ids = new ArraySet<>(); int saveType = saveInfo.getType(); if (saveType == SaveInfo.SAVE_DATA_TYPE_GENERIC) { - for (Set<AutofillId> autofillIds: hintsToAutofillIdMap.values()) { + for (Set<AutofillId> autofillIds : hintsToAutofillIdMap.values()) { ids.addAll(autofillIds); } } else { Set<String> hints = HintsHelper.getHintsForSaveType(saveType); - for (Map.Entry<String, Set<AutofillId>> entry: hintsToAutofillIdMap.entrySet()) { + for (Map.Entry<String, Set<AutofillId>> entry : hintsToAutofillIdMap.entrySet()) { String hint = entry.getKey(); if (hints.contains(hint)) { ids.addAll(entry.getValue()); @@ -2039,9 +2167,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * A private class to hold & compute datasets to be shown - */ + /** A private class to hold & compute datasets to be shown */ private static class DatasetComputationContainer { // List of all autofill ids that have a corresponding datasets Set<AutofillId> mAutofillIds = new LinkedHashSet<>(); @@ -2103,9 +2229,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Computes datasets that are eligible to be shown based on provider detections. - * Datasets are populated in the provided container for them to be later merged with the - * PCC eligible datasets based on preference strategy. + * Computes datasets that are eligible to be shown based on provider detections. Datasets are + * populated in the provided container for them to be later merged with the PCC eligible + * datasets based on preference strategy. + * * @param response * @param container */ @@ -2210,9 +2337,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Computes datasets that are eligible to be shown based on PCC detections. - * Datasets are populated in the provided container for them to be later merged with the - * provider eligible datasets based on preference strategy. + * Computes datasets that are eligible to be shown based on PCC detections. Datasets are + * populated in the provided container for them to be later merged with the provider eligible + * datasets based on preference strategy. + * * @param response * @param container */ @@ -2267,14 +2395,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // For that, there has to be a datatype detected by PCC, and the dataset // for that datatype provided by the provider. AutofillId autofillId = dataset.getFieldIds().get(j); - if (!mClassificationState.mClassificationCombinedHintsMap - .containsKey(autofillId)) { + if (!mClassificationState.mClassificationCombinedHintsMap.containsKey( + autofillId)) { additionalEligibleAutofillIds.add(autofillId); additionalDatasetAutofillIds.add(autofillId); // For each of the field, copy over values. - copyFieldsFromDataset(dataset, j, autofillId, fieldIds, fieldValues, - fieldPresentations, fieldDialogPresentations, - fieldInlinePresentations, fieldInlineTooltipPresentations, + copyFieldsFromDataset( + dataset, + j, + autofillId, + fieldIds, + fieldValues, + fieldPresentations, + fieldDialogPresentations, + fieldInlinePresentations, + fieldInlineTooltipPresentations, fieldFilters); } continue; @@ -2292,9 +2427,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState eligibleAutofillIds.add(autofillId); datasetAutofillIds.add(autofillId); // For each of the field, copy over values. - copyFieldsFromDataset(dataset, j, autofillId, fieldIds, fieldValues, - fieldPresentations, fieldDialogPresentations, - fieldInlinePresentations, fieldInlineTooltipPresentations, + copyFieldsFromDataset( + dataset, + j, + autofillId, + fieldIds, + fieldValues, + fieldPresentations, + fieldDialogPresentations, + fieldInlinePresentations, + fieldInlineTooltipPresentations, fieldFilters); } } @@ -2364,8 +2506,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState fieldPresentations.add(dataset.getFieldPresentation(index)); fieldDialogPresentations.add(dataset.getFieldDialogPresentation(index)); fieldInlinePresentations.add(dataset.getFieldInlinePresentation(index)); - fieldInlineTooltipPresentations.add( - dataset.getFieldInlineTooltipPresentation(index)); + fieldInlineTooltipPresentations.add(dataset.getFieldInlineTooltipPresentation(index)); fieldFilters.add(dataset.getFilter(index)); } @@ -2395,16 +2536,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState unregisterDelayedFillBroadcastLocked(); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId - + ") rejected - session: " + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onFillRequestFailureOrTimeout(req=" + + requestId + + ") rejected - session: " + + id + + " destroyed"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; } if (sDebug) { - Slog.d(TAG, "finishing session due to service " - + (timedOut ? "timeout" : "failure")); + Slog.d( + TAG, + "finishing session due to service " + (timedOut ? "timeout" : "failure")); } mService.resetLastResponse(); mLastFillDialogTriggerIds = null; @@ -2418,12 +2565,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int targetSdk = mService.getTargedSdkLocked(); if (targetSdk >= Build.VERSION_CODES.Q) { showMessage = false; - Slog.w(TAG, "onFillRequestFailureOrTimeout(): not showing '" + message - + "' because service's targetting API " + targetSdk); + Slog.w( + TAG, + "onFillRequestFailureOrTimeout(): not showing '" + + message + + "' because service's targeting API " + + targetSdk); } if (message != null) { - requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, - message.length()); + requestLog.addTaggedData( + MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length()); } } @@ -2445,8 +2596,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis(); mFillResponseEventLogger.logAndEndEvent(); } - notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED, - /* autofillableIds= */ null); + notifyUnavailableToClient( + AutofillManager.STATE_UNKNOWN_FAILED, /* autofillableIds= */ null); if (showMessage) { getUiForShowing().showError(message, this); } @@ -2455,8 +2606,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override - public void onSaveRequestSuccess(@NonNull String servicePackageName, - @Nullable IntentSender intentSender) { + public void onSaveRequestSuccess( + @NonNull String servicePackageName, @Nullable IntentSender intentSender) { synchronized (mLock) { mSessionFlags.mShowingSaveUi = false; // Log onSaveRequest result. @@ -2464,16 +2615,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSaveEventLogger.maybeSetLatencySaveFinishMillis(); mSaveEventLogger.logAndEndEvent(); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onSaveRequestSuccess() rejected - session: " + + id + + " destroyed"); return; } } - LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) - .setType(intentSender == null ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_OPEN); + LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) + .setType( + intentSender == null + ? MetricsEvent.TYPE_SUCCESS + : MetricsEvent.TYPE_OPEN); mMetricsLogger.write(log); - if (intentSender != null) { if (sDebug) Slog.d(TAG, "Starting intent sender on save()"); startIntentSenderAndFinishSession(intentSender); @@ -2485,8 +2642,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override - public void onSaveRequestFailure(@Nullable CharSequence message, - @NonNull String servicePackageName) { + public void onSaveRequestFailure( + @Nullable CharSequence message, @NonNull String servicePackageName) { boolean showMessage = !TextUtils.isEmpty(message); synchronized (mLock) { @@ -2495,28 +2652,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSaveEventLogger.maybeSetLatencySaveFinishMillis(); mSaveEventLogger.logAndEndEvent(); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onSaveRequestFailure() rejected - session: " + + id + + " destroyed"); return; } if (showMessage) { final int targetSdk = mService.getTargedSdkLocked(); if (targetSdk >= Build.VERSION_CODES.Q) { showMessage = false; - Slog.w(TAG, "onSaveRequestFailure(): not showing '" + message - + "' because service's targetting API " + targetSdk); + Slog.w( + TAG, + "onSaveRequestFailure(): not showing '" + + message + + "' because service's targeting API " + + targetSdk); } } } final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) - .setType(MetricsEvent.TYPE_FAILURE); + .setType(MetricsEvent.TYPE_FAILURE); if (message != null) { log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length()); } mMetricsLogger.write(log); - if (showMessage) { getUiForShowing().showError(message, this); } @@ -2525,8 +2688,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override - public void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse - convertCredentialResponse) { + public void onConvertCredentialRequestSuccess( + @NonNull ConvertCredentialResponse convertCredentialResponse) { Dataset dataset = convertCredentialResponse.getDataset(); Bundle clientState = convertCredentialResponse.getClientState(); if (dataset != null) { @@ -2534,15 +2697,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (clientState != null) { requestId = clientState.getInt(EXTRA_AUTOFILL_REQUEST_ID); } else { - Slog.e(TAG, "onConvertCredentialRequestSuccess(): client state is null, this " - + "would cause loss in logging."); + Slog.e( + TAG, + "onConvertCredentialRequestSuccess(): client state is null, this " + + "would cause loss in logging."); } // TODO: Add autofill related logging; consider whether to log the index - fill(requestId, /* datasetIndex=*/ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET); + fill(requestId, /* datasetIndex= */ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET); } else { // TODO: Add logging to log this error case - Slog.e(TAG, "onConvertCredentialRequestSuccess(): dataset inside response is " - + "null"); + Slog.e( + TAG, + "onConvertCredentialRequestSuccess(): dataset inside response is " + "null"); } } @@ -2550,11 +2716,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Gets the {@link FillContext} for a request. * * @param requestId The id of the request - * * @return The context or {@code null} if there is no context */ @GuardedBy("mLock") - @Nullable private FillContext getFillContextByRequestIdLocked(int requestId) { + @Nullable + private FillContext getFillContextByRequestIdLocked(int requestId) { if (mContexts == null) { return null; } @@ -2582,19 +2748,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // AutoFillUiCallback @Override - public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras, - int uiType) { + public void authenticate( + int requestId, int datasetIndex, IntentSender intent, Bundle extras, int uiType) { if (sDebug) { - Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex - + "; intentSender=" + intent); + Slog.d( + TAG, + "authenticate(): requestId=" + + requestId + + "; datasetIdx=" + + datasetIndex + + "; intentSender=" + + intent); } final Intent fillInIntent; synchronized (mLock) { mPresentationStatsEventLogger.maybeSetAuthenticationType( - AUTHENTICATION_TYPE_FULL_AUTHENTICATION); + AUTHENTICATION_TYPE_FULL_AUTHENTICATION); if (mDestroyed) { - Slog.w(TAG, "Call to Session#authenticate() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#authenticate() rejected - session: " + id + " destroyed"); return; } fillInIntent = createAuthFillInIntentLocked(requestId, extras); @@ -2607,10 +2780,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mService.setAuthenticationSelected(id, mClientState, uiType); final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex); - mHandler.sendMessage(obtainMessage( - Session::startAuthentication, - this, authenticationId, intent, fillInIntent, - /* authenticateInline= */ uiType == UI_TYPE_INLINE)); + mHandler.sendMessage( + obtainMessage( + Session::startAuthentication, + this, + authenticationId, + intent, + fillInIntent, + /* authenticateInline= */ uiType == UI_TYPE_INLINE)); } // AutoFillUiCallback @@ -2618,14 +2795,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void fill(int requestId, int datasetIndex, Dataset dataset, int uiType) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#fill() rejected - session: " - + id + " destroyed"); + Slog.w(TAG, "Call to Session#fill() rejected - session: " + id + " destroyed"); return; } } - mHandler.sendMessage(obtainMessage( - Session::autoFill, - this, requestId, datasetIndex, dataset, true, uiType)); + mHandler.sendMessage( + obtainMessage( + Session::autoFill, this, requestId, datasetIndex, dataset, true, uiType)); } // AutoFillUiCallback @@ -2633,15 +2809,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void save() { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#save() rejected - session: " - + id + " destroyed"); + Slog.w(TAG, "Call to Session#save() rejected - session: " + id + " destroyed"); return; } mSaveEventLogger.maybeSetLatencySaveRequestMillis(); } - mHandler.sendMessage(obtainMessage( - AutofillManagerServiceImpl::handleSessionSave, - mService, this)); + mHandler.sendMessage( + obtainMessage(AutofillManagerServiceImpl::handleSessionSave, mService, this)); } // AutoFillUiCallback @@ -2650,13 +2824,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { mSessionFlags.mShowingSaveUi = false; if (mDestroyed) { - Slog.w(TAG, "Call to Session#cancelSave() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#cancelSave() rejected - session: " + id + " destroyed"); return; } } - mHandler.sendMessage(obtainMessage( - Session::removeFromService, this)); + mHandler.sendMessage(obtainMessage(Session::removeFromService, this)); } // AutofillUiCallback @@ -2692,26 +2866,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // AutoFillUiCallback @Override - public void requestShowFillUi(AutofillId id, int width, int height, - IAutofillWindowPresenter presenter) { + public void requestShowFillUi( + AutofillId id, int width, int height, IAutofillWindowPresenter presenter) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#requestShowFillUi() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#requestShowFillUi() rejected - session: " + + id + + " destroyed"); return; } if (id.equals(mCurrentViewId)) { try { final ViewState view = mViewStates.get(id); - mClient.requestShowFillUi(this.id, id, width, height, view.getVirtualBounds(), - presenter); + mClient.requestShowFillUi( + this.id, id, width, height, view.getVirtualBounds(), presenter); } catch (RemoteException e) { Slog.e(TAG, "Error requesting to show fill UI", e); } } else { if (sDebug) { - Slog.d(TAG, "Do not show full UI on " + id + " as it is not the current view (" - + mCurrentViewId + ") anymore"); + Slog.d( + TAG, + "Do not show full UI on " + + id + + " as it is not the current view (" + + mCurrentViewId + + ") anymore"); } } } @@ -2722,8 +2904,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#dispatchUnhandledKey() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#dispatchUnhandledKey() rejected - session: " + + id + + " destroyed"); return; } if (id.equals(mCurrentViewId)) { @@ -2733,8 +2918,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.e(TAG, "Error requesting to dispatch unhandled key", e); } } else { - Slog.w(TAG, "Do not dispatch unhandled key on " + id - + " as it is not the current view (" + mCurrentViewId + ") anymore"); + Slog.w( + TAG, + "Do not dispatch unhandled key on " + + id + + " as it is not the current view (" + + mCurrentViewId + + ") anymore"); } } } @@ -2790,17 +2980,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void startIntentSender(IntentSender intentSender, Intent intent) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#startIntentSender() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#startIntentSender() rejected - session: " + + id + + " destroyed"); return; } if (intent == null) { removeFromServiceLocked(); } } - mHandler.sendMessage(obtainMessage( - Session::doStartIntentSender, - this, intentSender, intent)); + mHandler.sendMessage( + obtainMessage(Session::doStartIntentSender, this, intentSender, intent)); } // AutoFillUiCallback @@ -2865,13 +3057,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void setAuthenticationResultLocked(Bundle data, int authenticationId) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#setAuthenticationResultLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#setAuthenticationResultLocked() rejected - session: " + + id + + " destroyed"); return; } if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): id= " + authenticationId - + ", data=" + data); + Slog.d( + TAG, + "setAuthenticationResultLocked(): id= " + authenticationId + ", data=" + data); } final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId); if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) { @@ -2890,9 +3086,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState removeFromService(); return; } - final FillResponse authenticatedResponse = mRequestId.isSecondaryProvider(requestId) - ? mSecondaryResponses.get(requestId) - : mResponses.get(requestId); + final FillResponse authenticatedResponse = + mRequestId.isSecondaryProvider(requestId) + ? mSecondaryResponses.get(requestId) + : mResponses.get(requestId); if (authenticatedResponse == null || data == null) { Slog.w(TAG, "no authenticated response"); mPresentationStatsEventLogger.maybeSetAuthenticationResult( @@ -2902,8 +3099,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId( - authenticationId); + final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId(authenticationId); Dataset dataset = null; // Authenticated a dataset - reset view state regardless if we got a response or a dataset if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) { @@ -2922,33 +3118,45 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionFlags.mExpiredResponse = false; final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT); - final GetCredentialException exception = data.getSerializable( - CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, - GetCredentialException.class); + final GetCredentialException exception = + data.getSerializable( + CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, + GetCredentialException.class); final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE); if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result - + ", clientState=" + newClientState + ", authenticationId=" + authenticationId); - } - if (Flags.autofillCredmanDevIntegration() && exception != null + Slog.d( + TAG, + "setAuthenticationResultLocked(): result=" + + result + + ", clientState=" + + newClientState + + ", authenticationId=" + + authenticationId); + } + if (Flags.autofillCredmanDevIntegration() + && exception != null && !exception.getType().equals(GetCredentialException.TYPE_USER_CANCELED)) { if (dataset != null && dataset.getFieldIds().size() == 1) { if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): result returns with" - + "Credential Manager Exception"); + Slog.d( + TAG, + "setAuthenticationResultLocked(): result returns with" + + "Credential Manager Exception"); } AutofillId autofillId = dataset.getFieldIds().get(0); - sendCredentialManagerResponseToApp(/*response=*/ null, - (GetCredentialException) exception, autofillId); + sendCredentialManagerResponseToApp( + /* response= */ null, (GetCredentialException) exception, autofillId); } return; } if (result instanceof FillResponse) { if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): received FillResponse from" - + " authentication flow"); + Slog.d( + TAG, + "setAuthenticationResultLocked(): received FillResponse from" + + " authentication flow"); } logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED); mPresentationStatsEventLogger.maybeSetAuthenticationResult( @@ -2963,33 +3171,40 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (dataset != null && dataset.getFieldIds().size() == 1) { AutofillId autofillId = dataset.getFieldIds().get(0); if (sDebug) { - Slog.d(TAG, "Received GetCredentialResponse from authentication flow," - + "for autofillId: " + autofillId); + Slog.d( + TAG, + "Received GetCredentialResponse from authentication flow," + + "for autofillId: " + + autofillId); } - sendCredentialManagerResponseToApp(response, - /*exception=*/ null, autofillId); + sendCredentialManagerResponseToApp(response, /* exception= */ null, autofillId); } } else if (Flags.autofillCredmanIntegration()) { - Dataset datasetFromCredentialResponse = getDatasetFromCredentialResponse( - (GetCredentialResponse) result); + Dataset datasetFromCredentialResponse = + getDatasetFromCredentialResponse((GetCredentialResponse) result); if (datasetFromCredentialResponse != null) { - autoFill(requestId, datasetIdx, datasetFromCredentialResponse, - false, UI_TYPE_UNKNOWN); + autoFill( + requestId, + datasetIdx, + datasetFromCredentialResponse, + false, + UI_TYPE_UNKNOWN); } } } else if (result instanceof Dataset) { if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): received Dataset from" - + " authentication flow"); + Slog.d( + TAG, + "setAuthenticationResultLocked(): received Dataset from" + + " authentication flow"); } if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) { - logAuthenticationStatusLocked(requestId, - MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED); + logAuthenticationStatusLocked( + requestId, MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED); mPresentationStatsEventLogger.maybeSetAuthenticationResult( AUTHENTICATION_RESULT_SUCCESS); if (newClientState != null) { - if (sDebug) - Slog.d(TAG, "Updating client state from auth dataset"); + if (sDebug) Slog.d(TAG, "Updating client state from auth dataset"); mClientState = newClientState; } Dataset datasetFromResult = getEffectiveDatasetForAuthentication((Dataset) result); @@ -2999,10 +3214,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } autoFill(requestId, datasetIdx, datasetFromResult, false, UI_TYPE_UNKNOWN); } else { - Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id " - + authenticationId); - logAuthenticationStatusLocked(requestId, - MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION); + Slog.w( + TAG, + "invalid index (" + + datasetIdx + + ") for authentication id " + + authenticationId); + logAuthenticationStatusLocked( + requestId, MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION); mPresentationStatsEventLogger.maybeSetAuthenticationResult( AUTHENTICATION_RESULT_FAILURE); } @@ -3010,8 +3229,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (result != null) { Slog.w(TAG, "service returned invalid auth type: " + result); } - logAuthenticationStatusLocked(requestId, - MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION); + logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION); mPresentationStatsEventLogger.maybeSetAuthenticationResult( AUTHENTICATION_RESULT_FAILURE); processNullResponseLocked(requestId, 0); @@ -3036,8 +3254,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "DBG: authenticated effective response: " + response); } if (response == null || response.getDatasets().size() == 0) { - Log.wtf(TAG, "No datasets in fill response on authentication. response = " - + (response == null ? "null" : response.toString())); + Log.wtf( + TAG, + "No datasets in fill response on authentication. response = " + + (response == null ? "null" : response.toString())); return authenticatedDataset; } List<Dataset> datasets = response.getDatasets(); @@ -3047,8 +3267,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState for (Dataset dataset : datasets) { if (!dataset.getFieldIds().isEmpty()) { for (int i = 0; i < dataset.getFieldIds().size(); i++) { - builder.setField(dataset.getFieldIds().get(i), - new Field.Builder().setValue(dataset.getFieldValues().get(i)) + builder.setField( + dataset.getFieldIds().get(i), + new Field.Builder() + .setValue(dataset.getFieldValues().get(i)) .build()); } } @@ -3063,12 +3285,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Returns whether the dataset returned from the authentication result is ephemeral or not. - * See {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more - * information. + * Returns whether the dataset returned from the authentication result is ephemeral or not. See + * {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more information. */ - private static boolean isAuthResultDatasetEphemeral(@Nullable Dataset oldDataset, - @NonNull Bundle authResultData) { + private static boolean isAuthResultDatasetEphemeral( + @Nullable Dataset oldDataset, @NonNull Bundle authResultData) { if (authResultData.containsKey( AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) { return authResultData.getBoolean( @@ -3079,11 +3300,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * A dataset can potentially have multiple fields, and it's possible that some of the fields' - * has inline presentation and some don't. It's also possible that some of the fields' - * inline presentation is pinned and some isn't. So the concept of whether a dataset is - * pinned or not is ill-defined. Here we say a dataset is pinned if any of the field has a - * pinned inline presentation in the dataset. It's not ideal but hopefully it is sufficient - * for most of the cases. + * has inline presentation and some don't. It's also possible that some of the fields' inline + * presentation is pinned and some isn't. So the concept of whether a dataset is pinned or not + * is ill-defined. Here we say a dataset is pinned if any of the field has a pinned inline + * presentation in the dataset. It's not ideal but hopefully it is sufficient for most of the + * cases. */ private static boolean isPinnedDataset(@Nullable Dataset dataset) { if (dataset != null && dataset.getFieldIds() != null) { @@ -3100,16 +3321,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void setAuthenticationResultForAugmentedAutofillLocked(Bundle data, int authId) { - final Dataset dataset = (data == null) ? null : - data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT, android.service.autofill.Dataset.class); + final Dataset dataset = + (data == null) + ? null + : data.getParcelable( + AutofillManager.EXTRA_AUTHENTICATION_RESULT, + android.service.autofill.Dataset.class); if (sDebug) { - Slog.d(TAG, "Auth result for augmented autofill: sessionId=" + id - + ", authId=" + authId + ", dataset=" + dataset); - } - final AutofillId fieldId = (dataset != null && dataset.getFieldIds().size() == 1) - ? dataset.getFieldIds().get(0) : null; - final AutofillValue value = (dataset != null && dataset.getFieldValues().size() == 1) - ? dataset.getFieldValues().get(0) : null; + Slog.d( + TAG, + "Auth result for augmented autofill: sessionId=" + + id + + ", authId=" + + authId + + ", dataset=" + + dataset); + } + final AutofillId fieldId = + (dataset != null && dataset.getFieldIds().size() == 1) + ? dataset.getFieldIds().get(0) + : null; + final AutofillValue value = + (dataset != null && dataset.getFieldValues().size() == 1) + ? dataset.getFieldValues().get(0) + : null; final ClipData content = (dataset != null) ? dataset.getFieldContent() : null; if (fieldId == null || (value == null && content == null)) { if (sDebug) { @@ -3154,8 +3389,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Fill the value into the field. if (sDebug) { - Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value - + ", content=" + content); + Slog.d( + TAG, + "Filling after auth: fieldId=" + + fieldId + + ", value=" + + value + + ", content=" + + content); } try { if (content != null) { @@ -3164,8 +3405,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mClient.autofill(id, dataset.getFieldIds(), dataset.getFieldValues(), true); } } catch (RemoteException e) { - Slog.w(TAG, "Error filling after auth: fieldId=" + fieldId + ", value=" + value - + ", content=" + content, e); + Slog.w( + TAG, + "Error filling after auth: fieldId=" + + fieldId + + ", value=" + + value + + ", content=" + + content, + e); } // Clear the suggestions since the user already accepted one of them. @@ -3175,8 +3423,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void setHasCallbackLocked(boolean hasIt) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#setHasCallbackLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#setHasCallbackLocked() rejected - session: " + + id + + " destroyed"); return; } mHasCallback = hasIt; @@ -3185,9 +3436,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") @Nullable private FillResponse getLastResponseLocked(@Nullable String logPrefixFmt) { - final String logPrefix = sDebug && logPrefixFmt != null - ? String.format(logPrefixFmt, this.id) - : null; + final String logPrefix = + sDebug && logPrefixFmt != null ? String.format(logPrefixFmt, this.id) : null; if (mContexts == null) { if (logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts"); return null; @@ -3204,16 +3454,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int lastResponseIdx = getLastResponseIndexLocked(); if (lastResponseIdx < 0) { if (logPrefix != null) { - Slog.w(TAG, logPrefix + ": did not get last response. mResponses=" + mResponses - + ", mViewStates=" + mViewStates); + Slog.w( + TAG, + logPrefix + + ": did not get last response. mResponses=" + + mResponses + + ", mViewStates=" + + mViewStates); } return null; } final FillResponse response = mResponses.valueAt(lastResponseIdx); if (sVerbose && logPrefix != null) { - Slog.v(TAG, logPrefix + ": mResponses=" + mResponses + ", mContexts=" + mContexts - + ", mViewStates=" + mViewStates); + Slog.v( + TAG, + logPrefix + + ": mResponses=" + + mResponses + + ", mContexts=" + + mContexts + + ", mViewStates=" + + mViewStates); } return response; } @@ -3232,9 +3494,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Get statistic information of save info in current session. Specifically - * 1. how many save info the current session has. - * 2. How many distinct save data types current session has. + * Get statistic information of save info in current session. Specifically 1. how many save info + * the current session has. 2. How many distinct save data types current session has. * * @return SaveInfoStats returns the above two number in a SaveInfoStats object */ @@ -3255,12 +3516,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ public void logContextCommitted() { if (sVerbose) { - Slog.v(TAG, "logContextCommitted (" + id + "): commit_reason:" + COMMIT_REASON_UNKNOWN - + " no_save_reason:" + Event.NO_SAVE_UI_REASON_NONE); - } - mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, - Event.NO_SAVE_UI_REASON_NONE, - COMMIT_REASON_UNKNOWN)); + Slog.v( + TAG, + "logContextCommitted (" + + id + + "): commit_reason:" + + COMMIT_REASON_UNKNOWN + + " no_save_reason:" + + Event.NO_SAVE_UI_REASON_NONE); + } + mHandler.sendMessage( + obtainMessage( + Session::handleLogContextCommitted, + this, + Event.NO_SAVE_UI_REASON_NONE, + COMMIT_REASON_UNKNOWN)); synchronized (mLock) { logAllEventsLocked(COMMIT_REASON_UNKNOWN); } @@ -3274,16 +3544,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * @param saveDialogNotShowReason The reason why a save dialog was not shown. * @param commitReason The reason why context is committed. */ - @GuardedBy("mLock") - public void logContextCommittedLocked(@NoSaveReason int saveDialogNotShowReason, - @AutofillCommitReason int commitReason) { + public void logContextCommittedLocked( + @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { if (sVerbose) { - Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason - + " no_save_reason:" + saveDialogNotShowReason); - } - mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, - saveDialogNotShowReason, commitReason)); + Slog.v( + TAG, + "logContextCommittedLocked (" + + id + + "): commit_reason:" + + commitReason + + " no_save_reason:" + + saveDialogNotShowReason); + } + mHandler.sendMessage( + obtainMessage( + Session::handleLogContextCommitted, + this, + saveDialogNotShowReason, + commitReason)); mSessionCommittedEventLogger.maybeSetCommitReason(commitReason); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); @@ -3295,8 +3574,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE); } - private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason, - @AutofillCommitReason int commitReason) { + private void handleLogContextCommitted( + @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { final FillResponse lastResponse; synchronized (mLock) { lastResponse = getLastResponseLocked("logContextCommited(%s)"); @@ -3326,31 +3605,42 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Sets field classification scores if (userData != null && fcStrategy != null) { - logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason, - commitReason); + logFieldClassificationScore( + fcStrategy, userData, saveDialogNotShowReason, commitReason); } else { logContextCommitted(null, null, saveDialogNotShowReason, commitReason); } } - private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds, + private void logContextCommitted( + @Nullable ArrayList<AutofillId> detectedFieldIds, @Nullable ArrayList<FieldClassification> detectedFieldClassifications, @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { synchronized (mLock) { - logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason, commitReason); + logContextCommittedLocked( + detectedFieldIds, + detectedFieldClassifications, + saveDialogNotShowReason, + commitReason); } } @GuardedBy("mLock") - private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds, + private void logContextCommittedLocked( + @Nullable ArrayList<AutofillId> detectedFieldIds, @Nullable ArrayList<FieldClassification> detectedFieldClassifications, @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { if (sVerbose) { - Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason - + " no_save_reason:" + saveDialogNotShowReason); + Slog.v( + TAG, + "logContextCommittedLocked (" + + id + + "): commit_reason:" + + commitReason + + " no_save_reason:" + + saveDialogNotShowReason); } final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)"); if (lastResponse == null) return; @@ -3423,8 +3713,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue currentValue = viewState.getCurrentValue(); if (autofilledValue != null && autofilledValue.equals(currentValue)) { if (sDebug) { - Slog.d(TAG, "logContextCommitted(): ignoring changed " + viewState - + " because it has same value that was autofilled"); + Slog.d( + TAG, + "logContextCommitted(): ignoring changed " + + viewState + + " because it has same value that was autofilled"); } continue; } @@ -3442,8 +3735,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue currentValue = viewState.getCurrentValue(); if (currentValue == null) { if (sDebug) { - Slog.d(TAG, "logContextCommitted(): skipping view without current " - + "value ( " + viewState + ")"); + Slog.d( + TAG, + "logContextCommitted(): skipping view without current " + + "value ( " + + viewState + + ")"); } continue; } @@ -3455,7 +3752,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final List<Dataset> datasets = response.getDatasets(); if (datasets == null || datasets.isEmpty()) { if (sVerbose) { - Slog.v(TAG, "logContextCommitted() no datasets at " + j); + Slog.v(TAG, "logContextCommitted() no datasets at " + j); } } else { for (int k = 0; k < datasets.size(); k++) { @@ -3463,8 +3760,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final String datasetId = dataset.getId(); if (datasetId == null) { if (sVerbose) { - Slog.v(TAG, "logContextCommitted() skipping idless " - + "dataset " + dataset); + Slog.v( + TAG, + "logContextCommitted() skipping idless " + + "dataset " + + dataset); } } else { final ArrayList<AutofillValue> values = @@ -3473,9 +3773,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue candidate = values.get(l); if (currentValue.equals(candidate)) { if (sDebug) { - Slog.d(TAG, "field " + viewState.id + " was " - + "manually filled with value set by " - + "dataset " + datasetId); + Slog.d( + TAG, + "field " + + viewState.id + + " was manually filled with" + + " value set by dataset " + + datasetId); } if (manuallyFilledIds == null) { manuallyFilledIds = new ArrayMap<>(); @@ -3524,10 +3828,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, ignoredDatasets, - changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, - manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, - mComponentName, mCompatMode, saveDialogNotShowReason); + mService.logContextCommittedLocked( + id, + mClientState, + mSelectedDatasetIds, + ignoredDatasets, + changedFieldIds, + changedDatasetIds, + manuallyFilledFieldIds, + manuallyFilledDatasetIds, + detectedFieldIds, + detectedFieldClassifications, + mComponentName, + mCompatMode, + saveDialogNotShowReason); mSessionCommittedEventLogger.maybeSetCommitReason(commitReason); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason); @@ -3537,7 +3851,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for * {@code fieldId} based on its {@code currentValue} and {@code userData}. */ - private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy, + private void logFieldClassificationScore( + @NonNull FieldClassificationStrategy fcStrategy, @NonNull FieldClassificationUserData userData, @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { @@ -3555,16 +3870,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (userValues == null || categoryIds == null || userValues.length != categoryIds.length) { final int valuesLength = userValues == null ? -1 : userValues.length; final int idsLength = categoryIds == null ? -1 : categoryIds.length; - Slog.w(TAG, "setScores(): user data mismatch: values.length = " - + valuesLength + ", ids.length = " + idsLength); + Slog.w( + TAG, + "setScores(): user data mismatch: values.length = " + + valuesLength + + ", ids.length = " + + idsLength); return; } final int maxFieldsSize = UserData.getMaxFieldClassificationIdsSize(); final ArrayList<AutofillId> detectedFieldIds = new ArrayList<>(maxFieldsSize); - final ArrayList<FieldClassification> detectedFieldClassifications = new ArrayList<>( - maxFieldsSize); + final ArrayList<FieldClassification> detectedFieldClassifications = + new ArrayList<>(maxFieldsSize); final Collection<ViewState> viewStates; synchronized (mLock) { @@ -3583,33 +3902,49 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Then use the results, asynchronously - final RemoteCallback callback = new RemoteCallback( - new LogFieldClassificationScoreOnResultListener( - this, - saveDialogNotShowReason, - commitReason, - viewsSize, - autofillIds, - userValues, - categoryIds, - detectedFieldIds, - detectedFieldClassifications)); - - fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds, - defaultAlgorithm, defaultArgs, algorithms, args); - } - - void handleLogFieldClassificationScore(@Nullable Bundle result, int saveDialogNotShowReason, - int commitReason, int viewsSize, AutofillId[] autofillIds, String[] userValues, - String[] categoryIds, ArrayList<AutofillId> detectedFieldIds, + final RemoteCallback callback = + new RemoteCallback( + new LogFieldClassificationScoreOnResultListener( + this, + saveDialogNotShowReason, + commitReason, + viewsSize, + autofillIds, + userValues, + categoryIds, + detectedFieldIds, + detectedFieldClassifications)); + + fcStrategy.calculateScores( + callback, + currentValues, + userValues, + categoryIds, + defaultAlgorithm, + defaultArgs, + algorithms, + args); + } + + void handleLogFieldClassificationScore( + @Nullable Bundle result, + int saveDialogNotShowReason, + int commitReason, + int viewsSize, + AutofillId[] autofillIds, + String[] userValues, + String[] categoryIds, + ArrayList<AutofillId> detectedFieldIds, ArrayList<FieldClassification> detectedFieldClassifications) { if (result == null) { if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results"); logContextCommitted(null, null, saveDialogNotShowReason, commitReason); return; } - final Scores scores = result.getParcelable(EXTRA_SCORES, - android.service.autofill.AutofillFieldClassificationService.Scores.class); + final Scores scores = + result.getParcelable( + EXTRA_SCORES, + android.service.autofill.AutofillFieldClassificationService.Scores.class); if (scores == null) { Slog.w(TAG, "No field classification score on " + result); return; @@ -3633,14 +3968,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final Float currentScore = scoresByField.get(categoryId); if (currentScore != null && currentScore > score) { if (sVerbose) { - Slog.v(TAG, "skipping score " + score - + " because it's less than " + currentScore); + Slog.v( + TAG, + "skipping score " + + score + + " because it's less than " + + currentScore); } continue; } if (sVerbose) { - Slog.v(TAG, "adding score " + score + " at index " + j + " and id " - + autofillId); + Slog.v( + TAG, + "adding score " + + score + + " at index " + + j + + " and id " + + autofillId); } scoresByField.put(categoryId, score); } else if (sVerbose) { @@ -3666,13 +4011,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e); return; } - logContextCommitted(detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason, commitReason); + logContextCommitted( + detectedFieldIds, + detectedFieldClassifications, + saveDialogNotShowReason, + commitReason); } /** - * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} - * when necessary. + * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} when + * necessary. * * <p>Note: It is necessary to call logContextCommitted() first before calling this method. */ @@ -3689,11 +4037,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull public SaveResult showSaveLocked() { if (mDestroyed) { - Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#showSaveLocked() rejected - session: " + id + " destroyed"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ false, Event.NO_SAVE_UI_REASON_NONE); } mSessionState = STATE_FINISHED; @@ -3705,12 +4056,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ if (mSessionFlags.mScreenHasCredmanField) { if (sVerbose) { - Slog.v(TAG, "Call to Session#showSaveLocked() rejected - " - + "there is credman field in screen"); + Slog.v( + TAG, + "Call to Session#showSaveLocked() rejected - " + + "there is credman field in screen"); } mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SCREEN_HAS_CREDMAN_FIELD); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_NONE); } @@ -3728,7 +4083,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_NO_SAVE_INFO); } @@ -3737,7 +4094,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ false, Event.NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG); } @@ -3774,12 +4133,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Some apps clear the form before navigating to other activities. // If current value is empty, consider fall back to last cached // non-empty result first. - final AutofillValue candidateSaveValue = - viewState.getCandidateSaveValue(); + final AutofillValue candidateSaveValue = viewState.getCandidateSaveValue(); if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { if (sVerbose) { - Slog.v(TAG, "current value is empty, using cached last non-empty " - + "value instead"); + Slog.v( + TAG, + "current value is empty, using cached last non-empty " + + "value instead"); } value = candidateSaveValue; } else { @@ -3788,8 +4148,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue initialValue = getValueFromContextsLocked(id); if (initialValue != null) { if (sDebug) { - Slog.d(TAG, "Value of required field " + id + " didn't change; " - + "using initial value (" + initialValue + ") instead"); + Slog.d( + TAG, + "Value of required field " + + id + + " didn't change; " + + "using initial value (" + + initialValue + + ") instead"); } value = initialValue; } else { @@ -3821,8 +4187,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue initialValue = getValueFromContextsLocked(id); if (initialValue != null && initialValue.equals(value)) { if (sDebug) { - Slog.d(TAG, "id " + id + " is part of dataset but initial value " - + "didn't change: " + value); + Slog.d( + TAG, + "id " + + id + + " is part of dataset but initial value " + + "didn't change: " + + value); } changed = false; } else { @@ -3833,8 +4204,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (changed) { if (sDebug) { - Slog.d(TAG, "found a change on required " + id + ": " + filledValue - + " => " + value); + Slog.d( + TAG, + "found a change on required " + + id + + ": " + + filledValue + + " => " + + value); } atLeastOneChanged = true; } @@ -3844,8 +4221,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId[] optionalIds = saveInfo.getOptionalIds(); if (sVerbose) { - Slog.v(TAG, "allRequiredAreNotEmpty: " + allRequiredAreNotEmpty + " hasOptional: " - + (optionalIds != null)); + Slog.v( + TAG, + "allRequiredAreNotEmpty: " + + allRequiredAreNotEmpty + + " hasOptional: " + + (optionalIds != null)); } int saveDialogNotShowReason; if (!allRequiredAreNotEmpty) { @@ -3859,7 +4240,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // - if at least one required id changed but it was not part of a filled dataset, we // need to check if an optional id is part of a filled datased (in which case we show // Update instead of Save) - if (optionalIds!= null && (!atLeastOneChanged || !isUpdate)) { + if (optionalIds != null && (!atLeastOneChanged || !isUpdate)) { // No change on required ids yet, look for changes on optional ids. for (int i = 0; i < optionalIds.length; i++) { final AutofillId id = optionalIds[i]; @@ -3879,8 +4260,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState viewState.getCandidateSaveValue(); if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { if (sVerbose) { - Slog.v(TAG, "current value is empty, using cached last " - + "non-empty value instead"); + Slog.v( + TAG, + "current value is empty, using cached last " + + "non-empty value instead"); } currentValue = candidateSaveValue; } @@ -3897,8 +4280,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue filledValue = viewState.getAutofilledValue(); if (value != null && !value.equals(filledValue)) { if (sDebug) { - Slog.d(TAG, "found a change on optional " + id + ": " + filledValue - + " => " + value); + Slog.d( + TAG, + "found a change on optional " + + id + + ": " + + filledValue + + " => " + + value); } if (filledValue != null) { isUpdate = true; @@ -3907,12 +4296,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } atLeastOneChanged = true; } - } else { + } else { // Update current values cache based on initial value final AutofillValue initialValue = getValueFromContextsLocked(id); if (sDebug) { - Slog.d(TAG, "no current value for " + id + "; initial value is " - + initialValue); + Slog.d( + TAG, + "no current value for " + + id + + "; initial value is " + + initialValue); } if (initialValue != null) { currentValues.put(id, initialValue); @@ -3935,17 +4328,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { isValid = validator.isValid(this); if (sDebug) Slog.d(TAG, validator + " returned " + isValid); - log.setType(isValid - ? MetricsEvent.TYPE_SUCCESS - : MetricsEvent.TYPE_DISMISS); + log.setType( + isValid ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_DISMISS); } catch (Exception e) { Slog.e(TAG, "Not showing save UI because validation failed:", e); log.setType(MetricsEvent.TYPE_FAILURE); mMetricsLogger.write(log); mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_FIELD_VALIDATION_FAILED); + NO_SAVE_REASON_FIELD_VALIDATION_FAILED); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED); } @@ -3953,9 +4347,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (!isValid) { Slog.i(TAG, "not showing save UI because fields failed validation"); mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_FIELD_VALIDATION_FAILED); + NO_SAVE_REASON_FIELD_VALIDATION_FAILED); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED); } } @@ -3964,15 +4360,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // content. final List<Dataset> datasets = response.getDatasets(); if (datasets != null) { - datasets_loop: for (int i = 0; i < datasets.size(); i++) { + datasets_loop: + for (int i = 0; i < datasets.size(); i++) { final Dataset dataset = datasets.get(i); final ArrayMap<AutofillId, AutofillValue> datasetValues = Helper.getFields(dataset); if (sVerbose) { - Slog.v(TAG, "Checking if saved fields match contents of dataset #" + i - + ": " + dataset + "; savableIds=" + savableIds); + Slog.v( + TAG, + "Checking if saved fields match contents of dataset #" + + i + + ": " + + dataset + + "; savableIds=" + + savableIds); } - savable_ids_loop: for (int j = 0; j < savableIds.size(); j++) { + savable_ids_loop: + for (int j = 0; j < savableIds.size(); j++) { final AutofillId id = savableIds.valueAt(j); final AutofillValue currentValue = currentValues.get(id); if (currentValue == null) { @@ -3984,20 +4388,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue datasetValue = datasetValues.get(id); if (!currentValue.equals(datasetValue)) { if (sDebug) { - Slog.d(TAG, "found a dataset change on id " + id + ": from " - + datasetValue + " to " + currentValue); + Slog.d( + TAG, + "found a dataset change on id " + + id + + ": from " + + datasetValue + + " to " + + currentValue); } continue datasets_loop; } if (sVerbose) Slog.v(TAG, "no dataset changes for id " + id); } if (sDebug) { - Slog.d(TAG, "ignoring Save UI because all fields match contents of " - + "dataset #" + i + ": " + dataset); + Slog.d( + TAG, + "ignoring Save UI because all fields match contents of " + + "dataset #" + + i + + ": " + + dataset); } mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_DATASET_MATCH); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_DATASET_MATCH); } } @@ -4015,13 +4432,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState wtf(null, "showSaveLocked(): no service label or icon"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_NONE); } - getUiForShowing().showSaveUi(serviceLabel, serviceIcon, - mService.getServicePackageName(), saveInfo, this, - mComponentName, this, mContext, mPendingSaveUi, isUpdate, mCompatMode, - response.getShowSaveDialogIcon(), mSaveEventLogger); + getUiForShowing() + .showSaveUi( + serviceLabel, + serviceIcon, + mService.getServicePackageName(), + saveInfo, + this, + mComponentName, + this, + mContext, + mPendingSaveUi, + isUpdate, + mCompatMode, + response.getShowSaveDialogIcon(), + mSaveEventLogger); if (client != null) { try { client.setSaveUiState(id, true); @@ -4031,21 +4461,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } mSessionFlags.mShowingSaveUi = true; if (sDebug) { - Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for " - + id + "!"); + Slog.d( + TAG, + "Good news, everyone! All checks passed, show save UI for " + id + "!"); } - return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false, + return new SaveResult( + /* logSaveShown= */ true, + /* removeSession= */ false, Event.NO_SAVE_UI_REASON_NONE); } } // Nothing changed... if (sDebug) { - Slog.d(TAG, "showSaveLocked(" + id +"): with no changes, comes no responsibilities." - + "allRequiredAreNotNull=" + allRequiredAreNotEmpty - + ", atLeastOneChanged=" + atLeastOneChanged); + Slog.d( + TAG, + "showSaveLocked(" + + id + + "): with no changes, comes no responsibilities." + + "allRequiredAreNotNull=" + + allRequiredAreNotEmpty + + ", atLeastOneChanged=" + + atLeastOneChanged); } - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, - saveDialogNotShowReason); + return new SaveResult( + /* logSaveShown= */ false, /* removeSession= */ true, saveDialogNotShowReason); } private void logSaveShown() { @@ -4078,25 +4517,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return sanitized; } - /** - * Returns whether the session is currently showing the save UI - */ + /** Returns whether the session is currently showing the save UI */ @GuardedBy("mLock") boolean isSaveUiShowingLocked() { return mSessionFlags.mShowingSaveUi; } - /** - * Gets the latest non-empty value for the given id in the autofill contexts. - */ + /** Gets the latest non-empty value for the given id in the autofill contexts. */ @GuardedBy("mLock") @Nullable private ViewNode getViewNodeFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), - autofillId); + final ViewNode node = + Helper.findViewNodeByAutofillId(context.getStructure(), autofillId); if (node != null) { return node; } @@ -4104,22 +4539,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - /** - * Gets the latest non-empty value for the given id in the autofill contexts. - */ + /** Gets the latest non-empty value for the given id in the autofill contexts. */ @GuardedBy("mLock") @Nullable private AutofillValue getValueFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), - autofillId); + final ViewNode node = + Helper.findViewNodeByAutofillId(context.getStructure(), autofillId); if (node != null) { final AutofillValue value = node.getAutofillValue(); if (sDebug) { - Slog.d(TAG, "getValueFromContexts(" + this.id + "/" + autofillId + ") at " - + i + ": " + value); + Slog.d( + TAG, + "getValueFromContexts(" + + this.id + + "/" + + autofillId + + ") at " + + i + + ": " + + value); } if (value != null && !value.isEmpty()) { return value; @@ -4129,17 +4570,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - /** - * Gets the latest autofill options for the given id in the autofill contexts. - */ + /** Gets the latest autofill options for the given id in the autofill contexts. */ @GuardedBy("mLock") @Nullable private CharSequence[] getAutofillOptionsFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), - autofillId); + final ViewNode node = + Helper.findViewNodeByAutofillId(context.getStructure(), autofillId); if (node != null && node.getAutofillOptions() != null) { return node.getAutofillOptions(); } @@ -4160,7 +4599,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final FillContext context = mContexts.get(contextNum); final ViewNode[] nodes = - context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); + context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); if (sVerbose) Slog.v(TAG, "updateValuesForSaveLocked(): updating " + context); @@ -4191,8 +4630,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sanitizedValue != null) { node.updateAutofillValue(sanitizedValue); } else if (sDebug) { - Slog.d(TAG, "updateValuesForSaveLocked(): not updating field " + id - + " because it failed sanitization"); + Slog.d( + TAG, + "updateValuesForSaveLocked(): not updating field " + + id + + " because it failed sanitization"); } } @@ -4200,28 +4642,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState context.getStructure().sanitizeForParceling(false); if (sVerbose) { - Slog.v(TAG, "updateValuesForSaveLocked(): dumping structure of " + context - + " before calling service.save()"); + Slog.v( + TAG, + "updateValuesForSaveLocked(): dumping structure of " + + context + + " before calling service.save()"); context.getStructure().dump(false); } } } - /** - * Calls service when user requested save. - */ + /** Calls service when user requested save. */ @GuardedBy("mLock") void callSaveLocked() { if (mDestroyed) { - Slog.w(TAG, "Call to Session#callSaveLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#callSaveLocked() rejected - session: " + id + " destroyed"); mSaveEventLogger.maybeSetIsSaved(false); mSaveEventLogger.logAndEndEvent(); return; } if (mRemoteFillService == null) { - wtf(null, "callSaveLocked() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); + wtf( + null, + "callSaveLocked() called without a remote service. " + + "mForAugmentedAutofillOnly: %s", + mSessionFlags.mAugmentedAutofillOnly); mSaveEventLogger.maybeSetIsSaved(false); mSaveEventLogger.logAndEndEvent(); return; @@ -4241,7 +4688,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Remove pending fill requests as the session is finished. cancelCurrentRequestLocked(); - final ArrayList<FillContext> contexts = mergePreviousSessionLocked( /* forSave= */ true); + final ArrayList<FillContext> contexts = mergePreviousSessionLocked(/* forSave= */ true); FieldClassificationResponse fieldClassificationResponse = mClassificationState.mLastFieldClassificationResponse; @@ -4251,8 +4698,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mClientState == null) { mClientState = new Bundle(); } - mClientState.putParcelableArrayList(EXTRA_KEY_DETECTIONS, new ArrayList<>( - fieldClassificationResponse.getClassifications())); + mClientState.putParcelableArrayList( + EXTRA_KEY_DETECTIONS, + new ArrayList<>(fieldClassificationResponse.getClassifications())); } final SaveRequest saveRequest = new SaveRequest(contexts, mClientState, mSelectedDatasetIds); @@ -4270,11 +4718,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * from previous sessions that were asked by the service to be delayed (if any). * * <p>As a side-effect: + * * <ul> - * <li>If the current {@link #mClientState} is {@code null}, sets it with the last non- - * {@code null} client state from previous sessions. + * <li>If the current {@link #mClientState} is {@code null}, sets it with the last non- {@code + * null} client state from previous sessions. * <li>When {@code forSave} is {@code true}, calls {@link #updateValuesForSaveLocked()} in the - * previous sessions. + * previous sessions. * </ul> */ @NonNull @@ -4283,30 +4732,51 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<FillContext> contexts; if (previousSessions != null) { if (sDebug) { - Slog.d(TAG, "mergeSessions(" + this.id + "): Merging the content of " - + previousSessions.size() + " sessions for task " + taskId); + Slog.d( + TAG, + "mergeSessions(" + + this.id + + "): Merging the content of " + + previousSessions.size() + + " sessions for task " + + taskId); } contexts = new ArrayList<>(); for (int i = 0; i < previousSessions.size(); i++) { final Session previousSession = previousSessions.get(i); final ArrayList<FillContext> previousContexts = previousSession.mContexts; if (previousContexts == null) { - Slog.w(TAG, "mergeSessions(" + this.id + "): Not merging null contexts from " - + previousSession.id); + Slog.w( + TAG, + "mergeSessions(" + + this.id + + "): Not merging null contexts from " + + previousSession.id); continue; } if (forSave) { previousSession.updateValuesForSaveLocked(); } if (sDebug) { - Slog.d(TAG, "mergeSessions(" + this.id + "): adding " + previousContexts.size() - + " context from previous session #" + previousSession.id); + Slog.d( + TAG, + "mergeSessions(" + + this.id + + "): adding " + + previousContexts.size() + + " context from previous session #" + + previousSession.id); } contexts.addAll(previousContexts); if (mClientState == null && previousSession.mClientState != null) { if (sDebug) { - Slog.d(TAG, "mergeSessions(" + this.id + "): setting client state from " - + "previous session" + previousSession.id); + Slog.d( + TAG, + "mergeSessions(" + + this.id + + "): setting client state from " + + "previous session" + + previousSession.id); } mClientState = previousSession.mClientState; } @@ -4351,8 +4821,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // If it's not, then check if it should start a partition. if (shouldStartNewPartitionLocked(id, flags)) { if (sDebug) { - Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": " - + viewState.getStateAsString()); + Slog.d( + TAG, + "Starting partition or augmented request for view id " + + id + + ": " + + viewState.getStateAsString()); } // Fix to always let standard autofill start. // Sometimes activity contain IMPORTANT_FOR_AUTOFILL_NO fields which marks session as @@ -4363,8 +4837,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (sVerbose) { - Slog.v(TAG, "Not starting new partition for view " + id + ": " - + viewState.getStateAsString()); + Slog.v( + TAG, + "Not starting new partition for view " + + id + + ": " + + viewState.getStateAsString()); } return Optional.empty(); } @@ -4373,17 +4851,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Determines if a new partition should be started for an id. * * @param id The id of the view that is entered - * * @return {@code true} if a new partition should be started */ @GuardedBy("mLock") private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id, int flags) { final ViewState currentView = mViewStates.get(id); - SparseArray<FillResponse> responses = shouldRequestSecondaryProvider(flags) - ? mSecondaryResponses : mResponses; + SparseArray<FillResponse> responses = + shouldRequestSecondaryProvider(flags) ? mSecondaryResponses : mResponses; if (responses == null) { - return currentView != null && (currentView.getState() - & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0; + return currentView != null + && (currentView.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) + == 0; } if (mSessionFlags.mExpiredResponse) { @@ -4395,8 +4873,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int numResponses = responses.size(); if (numResponses >= AutofillManagerService.getPartitionMaxCount()) { - Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id - + " reached maximum of " + AutofillManagerService.getPartitionMaxCount()); + Slog.e( + TAG, + "Not starting a new partition on " + + id + + " because session " + + this.id + + " reached maximum of " + + AutofillManagerService.getPartitionMaxCount()); return false; } @@ -4437,8 +4921,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } boolean shouldRequestSecondaryProvider(int flags) { - if (!mService.isAutofillCredmanIntegrationEnabled() - || mSecondaryProviderHandler == null) { + if (!mService.isAutofillCredmanIntegrationEnabled() || mSecondaryProviderHandler == null) { return false; } if (mIsPrimaryCredential) { @@ -4452,8 +4935,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // 'Session.this.mLock', which is the same as mLock. @SuppressWarnings("GuardedBy") @GuardedBy("mLock") - void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int action, - int flags) { + void updateLocked( + AutofillId id, Rect virtualBounds, AutofillValue value, int action, int flags) { if (mDestroyed) { Slog.w(TAG, "updateLocked(" + id + "): rejected - session: destroyed"); return; @@ -4464,7 +4947,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "updateLocked(" + id + "): Set the response has expired."); } mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists( - NOT_SHOWN_REASON_VIEW_CHANGED); + NOT_SHOWN_REASON_VIEW_CHANGED); mPresentationStatsEventLogger.logAndEndEvent("ACTION_RESPONSE_EXPIRED"); return; } @@ -4474,23 +4957,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sVerbose) { Slog.v( TAG, - "updateLocked(" + id + "): " - + "id=" + this.id - + ", action=" + actionAsString(action) - + ", flags=" + flags - + ", mCurrentViewId=" + mCurrentViewId - + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse - + ", viewState=" + viewState); + "updateLocked(" + + id + + "): " + + "id=" + + this.id + + ", action=" + + actionAsString(action) + + ", flags=" + + flags + + ", mCurrentViewId=" + + mCurrentViewId + + ", mExpiredResponse=" + + mSessionFlags.mExpiredResponse + + ", viewState=" + + viewState); } if (viewState == null) { - if (action == ACTION_START_SESSION || action == ACTION_VALUE_CHANGED + if (action == ACTION_START_SESSION + || action == ACTION_VALUE_CHANGED || action == ACTION_VIEW_ENTERED) { if (sVerbose) Slog.v(TAG, "Creating viewState for " + id); boolean isIgnored = isIgnoredLocked(id); - viewState = new ViewState(id, this, - isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL, - mIsPrimaryCredential); + viewState = + new ViewState( + id, + this, + isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL, + mIsPrimaryCredential); mViewStates.put(id, viewState); // TODO(b/73648631): for optimization purposes, should also ignore if change is @@ -4521,7 +5016,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionFlags.mScreenHasCredmanField = true; } - switch(action) { + switch (action) { case ACTION_START_SESSION: // View is triggering autofill. mCurrentViewId = viewState.id; @@ -4545,14 +5040,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState case ACTION_VALUE_CHANGED: if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) { // Must cancel the session if the value of the URL bar changed - final String currentUrl = mUrlBar == null ? null - : mUrlBar.getText().toString().trim(); + final String currentUrl = + mUrlBar == null ? null : mUrlBar.getText().toString().trim(); if (currentUrl == null) { // Validation check - shouldn't happen. wtf(null, "URL bar value changed, but current value is null"); return; } - if (value == null || ! value.isText()) { + if (value == null || !value.isText()) { // Validation check - shouldn't happen. wtf(null, "URL bar value changed to null or non-text: %s", value); return; @@ -4567,8 +5062,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // are finished, as the URL bar changed callback is usually called before // the virtual views become invisible. if (sDebug) { - Slog.d(TAG, "Ignoring change on URL because session will finish when " - + "views are gone"); + Slog.d( + TAG, + "Ignoring change on URL because session will finish when " + + "views are gone"); } return; } @@ -4598,8 +5095,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // isSameViewEntered has some limitations, where it isn't considered same view when // autofill suggestions pop up, user selects, and the focus lands back on the view. // isSameViewAgain tries to overcome that situation. - final boolean isSameViewAgain = isSameViewEntered - || Objects.equals(mCurrentViewId, mPreviousNonNullEnteredViewId); + final boolean isSameViewAgain = + isSameViewEntered + || Objects.equals(mCurrentViewId, mPreviousNonNullEnteredViewId); if (mCurrentViewId != null) { mPreviousNonNullEnteredViewId = mCurrentViewId; } @@ -4655,8 +5153,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Trigger augmented autofill if applicable if ((flags & FLAG_MANUAL_REQUEST) == 0) { // Not a manual request - if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains( - id)) { + if (mAugmentedAutofillableIds != null + && mAugmentedAutofillableIds.contains(id)) { // Regular autofill handled the view and returned null response, but it // triggered augmented autofill if (!isSameViewEntered) { @@ -4664,16 +5162,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState triggerAugmentedAutofillLocked(flags); } else { if (sDebug) { - Slog.d(TAG, "skip augmented autofill for same view: " - + "same view entered"); + Slog.d( + TAG, + "skip augmented autofill for same view: " + + "same view entered"); } } return; } else if (mSessionFlags.mAugmentedAutofillOnly && isSameViewEntered) { // Regular autofill is disabled. if (sDebug) { - Slog.d(TAG, "skip augmented autofill for same view: " - + "standard autofill disabled."); + Slog.d( + TAG, + "skip augmented autofill for same view: " + + "standard autofill disabled."); } return; } @@ -4717,8 +5219,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // on the IME side if it arrives before the input view is finished on the IME. mInlineSessionController.resetInlineFillUiLocked(); - if ((viewState.getState() & - ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) != 0) { + if ((viewState.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) + != 0) { // View was exited before Inline Request sent back, do not set it to // null yet to let onHandleAssistData finish processing } else { @@ -4728,7 +5230,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // It's not necessary that there's no more presentation for this view. It could // be that the user chose some suggestion, in which case, view exits. mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( - NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED); + NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED); } break; default: @@ -4737,8 +5239,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void logPresentationStatsOnViewEnteredLocked(FillResponse response, - boolean isCredmanRequested) { + private void logPresentationStatsOnViewEnteredLocked( + FillResponse response, boolean isCredmanRequested) { mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested); mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId( mFieldClassificationIdSnapshot); @@ -4754,16 +5256,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void hideAugmentedAutofillLocked(@NonNull ViewState viewState) { - if ((viewState.getState() - & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) { + if ((viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) { viewState.resetState(ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL); cancelAugmentedAutofillLocked(); } } - /** - * Checks whether a view should be ignored. - */ + /** Checks whether a view should be ignored. */ @GuardedBy("mLock") private boolean isIgnoredLocked(AutofillId id) { // Always check the latest response only @@ -4782,22 +5281,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState && getSaveInfoLocked() != null) { final int length = viewState.getCurrentValue().getTextValue().length(); if (sDebug) { - Slog.d(TAG, "updateLocked(" + id + "): resetting value that was " - + length + " chars long"); - } - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length); + Slog.d( + TAG, + "updateLocked(" + + id + + "): resetting value that was " + + length + + " chars long"); + } + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length); mMetricsLogger.write(log); } } @GuardedBy("mLock") - private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value, - ViewState viewState, int flags) { + private void updateViewStateAndUiOnValueChangedLocked( + AutofillId id, AutofillValue value, ViewState viewState, int flags) { // Cache the last non-empty value for save purpose. Some apps clear the form before // navigating to other activities. - if (mIgnoreViewStateResetToEmpty && (value == null || value.isEmpty()) - && viewState.getCurrentValue() != null && viewState.getCurrentValue().isText() + if (mIgnoreViewStateResetToEmpty + && (value == null || value.isEmpty()) + && viewState.getCurrentValue() != null + && viewState.getCurrentValue().isText() && viewState.getCurrentValue().getTextValue() != null && viewState.getCurrentValue().getTextValue().length() > 1) { if (sVerbose) { @@ -4875,8 +5382,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * indicate the IME attempting to probe the potentially sensitive content of inline suggestions. */ @GuardedBy("mLock") - private void updateFilteringStateOnValueChangedLocked(@Nullable String newTextValue, - ViewState viewState) { + private void updateFilteringStateOnValueChangedLocked( + @Nullable String newTextValue, ViewState viewState) { if (newTextValue == null) { // Don't just return here, otherwise the IME can circumvent this logic using non-text // values. @@ -4901,18 +5408,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @Override - public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId, - @Nullable AutofillValue value, int flags) { + public void onFillReady( + @NonNull FillResponse response, + @NonNull AutofillId filledId, + @Nullable AutofillValue value, + int flags) { synchronized (mLock) { mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId( mFieldClassificationIdSnapshot); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onFillReady() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onFillReady() rejected - session: " + id + " destroyed"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED); mSaveEventLogger.logAndEndEvent(); mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( - NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY); + NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY); mPresentationStatsEventLogger.logAndEndEvent("on fill ready"); return; } @@ -4954,7 +5465,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } else { setFillDialogDisabled(); } - } if (response.supportsInlineSuggestions()) { @@ -4971,10 +5481,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - getUiForShowing().showFillUi(filledId, response, filterText, - mService.getServicePackageName(), mComponentName, - serviceLabel, serviceIcon, this, mContext, id, mCompatMode, - mService.getMaster().getMaxInputLengthForAutofill()); + getUiForShowing() + .showFillUi( + filledId, + response, + filterText, + mService.getServicePackageName(), + mComponentName, + serviceLabel, + serviceIcon, + this, + mContext, + id, + mCompatMode, + mService.getMaster().getMaxInputLengthForAutofill()); synchronized (mLock) { if (mUiShownTime == 0) { @@ -4983,21 +5503,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final long duration = mUiShownTime - mStartTime; if (sDebug) { - final StringBuilder msg = new StringBuilder("1st UI for ") - .append(mActivityToken) - .append(" shown in "); + final StringBuilder msg = + new StringBuilder("1st UI for ") + .append(mActivityToken) + .append(" shown in "); TimeUtils.formatDuration(duration, msg); Slog.d(TAG, msg.toString()); } - final StringBuilder historyLog = new StringBuilder("id=").append(id) - .append(" app=").append(mActivityToken) - .append(" svc=").append(mService.getServicePackageName()) - .append(" latency="); + final StringBuilder historyLog = + new StringBuilder("id=") + .append(id) + .append(" app=") + .append(mActivityToken) + .append(" svc=") + .append(mService.getServicePackageName()) + .append(" latency="); TimeUtils.formatDuration(duration, historyLog); mUiLatencyHistory.log(historyLog.toString()); - addTaggedDataToRequestLogLocked(response.getRequestId(), - MetricsEvent.FIELD_AUTOFILL_DURATION, duration); + addTaggedDataToRequestLogLocked( + response.getRequestId(), MetricsEvent.FIELD_AUTOFILL_DURATION, duration); } } } @@ -5052,8 +5577,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private boolean requestShowFillDialog(FillResponse response, - AutofillId filledId, String filterText, int flags) { + private boolean requestShowFillDialog( + FillResponse response, AutofillId filledId, String filterText, int flags) { if (!isFillDialogUiEnabled()) { // Unsupported fill dialog UI if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled"); @@ -5079,7 +5604,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed."); return false; } - } Drawable serviceIcon = null; @@ -5087,21 +5611,31 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState serviceIcon = getServiceIcon(response); } - getUiForShowing().showFillDialog(filledId, response, filterText, - mService.getServicePackageName(), mComponentName, serviceIcon, this, - id, mCompatMode, mPresentationStatsEventLogger, mLock); + getUiForShowing() + .showFillDialog( + filledId, + response, + filterText, + mService.getServicePackageName(), + mComponentName, + serviceIcon, + this, + id, + mCompatMode, + mPresentationStatsEventLogger, + mLock); return true; } /** - * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able - * to be fetched, use the default provider icon instead + * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able to + * be fetched, use the default provider icon instead * * @return Drawable of the provider icon, if it was able to be fetched. Null otherwise */ @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's - // actually the same object as mLock. - // TODO: Expose mService.mLock or redesign instead. + // actually the same object as mLock. + // TODO: Expose mService.mLock or redesign instead. @GuardedBy("mLock") private Drawable getServiceIcon(FillResponse response) { Drawable serviceIcon = null; @@ -5130,14 +5664,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Get the custom label that was passed through FillResponse. If the custom label - * wasn't able to be fetched, use the default provider icon instead + * Get the custom label that was passed through FillResponse. If the custom label wasn't able to + * be fetched, use the default provider icon instead * * @return Drawable of the provider icon, if it was able to be fetched. Null otherwise */ @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's - // actually the same object as mLock. - // TODO: Expose mService.mLock or redesign instead. + // actually the same object as mLock. + // TODO: Expose mService.mLock or redesign instead. @GuardedBy("mLock") private CharSequence getServiceLabel(FillResponse response) { CharSequence serviceLabel = null; @@ -5167,11 +5701,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return serviceLabel; } - /** - * Returns whether we made a request to show inline suggestions. - */ - private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response, - @Nullable String filterText) { + /** Returns whether we made a request to show inline suggestions. */ + private boolean requestShowInlineSuggestionsLocked( + @NonNull FillResponse response, @Nullable String filterText) { if (mCurrentViewId == null) { Log.w(TAG, "requestShowInlineSuggestionsLocked(): no view currently focused"); return false; @@ -5199,89 +5731,122 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } final InlineFillUi.InlineFillUiInfo inlineFillUiInfo = - new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId, - filterText, remoteRenderService, userId, id); - InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response, - new InlineFillUi.InlineSuggestionUiCallback() { - @Override - public void autofill(@NonNull Dataset dataset, int datasetIndex) { - fill(response.getRequestId(), datasetIndex, dataset, UI_TYPE_INLINE); - } + new InlineFillUi.InlineFillUiInfo( + inlineSuggestionsRequest.get(), + focusedId, + filterText, + remoteRenderService, + userId, + id); + InlineFillUi inlineFillUi = + InlineFillUi.forAutofill( + inlineFillUiInfo, + response, + new InlineFillUi.InlineSuggestionUiCallback() { + @Override + public void autofill(@NonNull Dataset dataset, int datasetIndex) { + fill( + response.getRequestId(), + datasetIndex, + dataset, + UI_TYPE_INLINE); + } - @Override - public void authenticate(int requestId, int datasetIndex) { - Session.this.authenticate(response.getRequestId(), datasetIndex, - response.getAuthentication(), response.getClientState(), - UI_TYPE_INLINE); - } + @Override + public void authenticate(int requestId, int datasetIndex) { + Session.this.authenticate( + response.getRequestId(), + datasetIndex, + response.getAuthentication(), + response.getClientState(), + UI_TYPE_INLINE); + } - @Override - public void startIntentSender(@NonNull IntentSender intentSender) { - Session.this.startIntentSender(intentSender, new Intent()); - } + @Override + public void startIntentSender(@NonNull IntentSender intentSender) { + Session.this.startIntentSender(intentSender, new Intent()); + } - @Override - public void onError() { - synchronized (mLock) { - mInlineSessionController.setInlineFillUiLocked( - InlineFillUi.emptyUi(focusedId)); - } - } + @Override + public void onError() { + synchronized (mLock) { + mInlineSessionController.setInlineFillUiLocked( + InlineFillUi.emptyUi(focusedId)); + } + } - @Override - public void onInflate() { - Session.this.onShown(UI_TYPE_INLINE, 1); - } - }, mService.getMaster().getMaxInputLengthForAutofill()); + @Override + public void onInflate() { + Session.this.onShown(UI_TYPE_INLINE, 1); + } + }, + mService.getMaster().getMaxInputLengthForAutofill()); return mInlineSessionController.setInlineFillUiLocked(inlineFillUi); } private ResultReceiver constructCredentialManagerCallback(int requestId) { - final ResultReceiver resultReceiver = new ResultReceiver(mHandler) { - final AutofillId mAutofillId = mCurrentViewId; - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode == SUCCESS_CREDMAN_SELECTOR) { - Slog.d(TAG, "onReceiveResult from Credential Manager " - + "bottom sheet with mCurrentViewId: " + mAutofillId); - GetCredentialResponse getCredentialResponse = - resultData.getParcelable( - CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, - GetCredentialResponse.class); - - if (Flags.autofillCredmanDevIntegration()) { - sendCredentialManagerResponseToApp(getCredentialResponse, - /*exception=*/ null, mAutofillId); - } else { - Dataset datasetFromCredential = getDatasetFromCredentialResponse( - getCredentialResponse); - if (datasetFromCredential != null) { - autoFill(requestId, /*datasetIndex=*/-1, - datasetFromCredential, false, - UI_TYPE_CREDMAN_BOTTOM_SHEET); + final ResultReceiver resultReceiver = + new ResultReceiver(mHandler) { + final AutofillId mAutofillId = mCurrentViewId; + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode == SUCCESS_CREDMAN_SELECTOR) { + Slog.d( + TAG, + "onReceiveResult from Credential Manager " + + "bottom sheet with mCurrentViewId: " + + mAutofillId); + GetCredentialResponse getCredentialResponse = + resultData.getParcelable( + CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, + GetCredentialResponse.class); + + if (Flags.autofillCredmanDevIntegration()) { + sendCredentialManagerResponseToApp( + getCredentialResponse, /* exception= */ null, mAutofillId); + } else { + Dataset datasetFromCredential = + getDatasetFromCredentialResponse(getCredentialResponse); + if (datasetFromCredential != null) { + autoFill( + requestId, + /* datasetIndex= */ -1, + datasetFromCredential, + false, + UI_TYPE_CREDMAN_BOTTOM_SHEET); + } + } + } else if (resultCode == FAILURE_CREDMAN_SELECTOR) { + String[] exception = + resultData.getStringArray( + CredentialProviderService + .EXTRA_GET_CREDENTIAL_EXCEPTION); + if (exception != null && exception.length >= 2) { + String errType = exception[0]; + String errMsg = exception[1]; + Slog.w( + TAG, + "Credman bottom sheet from pinned " + + "entry failed with: + " + + errType + + " , " + + errMsg); + sendCredentialManagerResponseToApp( + /* response= */ null, + new GetCredentialException(errType, errMsg), + mAutofillId); + } + } else { + Slog.d( + TAG, + "Unknown resultCode from credential " + + "manager bottom sheet: " + + resultCode); } } - } else if (resultCode == FAILURE_CREDMAN_SELECTOR) { - String[] exception = resultData.getStringArray( - CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION); - if (exception != null && exception.length >= 2) { - String errType = exception[0]; - String errMsg = exception[1]; - Slog.w(TAG, "Credman bottom sheet from pinned " - + "entry failed with: + " + errType + " , " - + errMsg); - sendCredentialManagerResponseToApp(/*response=*/ null, - new GetCredentialException(errType, errMsg), - mAutofillId); - } - } else { - Slog.d(TAG, "Unknown resultCode from credential " - + "manager bottom sheet: " + resultCode); - } - } - }; - ResultReceiver ipcFriendlyResultReceiver = - toIpcFriendlyResultReceiver(resultReceiver); + }; + ResultReceiver ipcFriendlyResultReceiver = toIpcFriendlyResultReceiver(resultReceiver); return ipcFriendlyResultReceiver; } @@ -5309,8 +5874,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private void notifyUnavailableToClient(int sessionFinishedState, - @Nullable ArrayList<AutofillId> autofillableIds) { + private void notifyUnavailableToClient( + int sessionFinishedState, @Nullable ArrayList<AutofillId> autofillableIds) { synchronized (mLock) { if (mCurrentViewId == null) return; try { @@ -5374,27 +5939,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (saveInfo.getRequiredIds() != null) { Collections.addAll(trackedViews, saveInfo.getRequiredIds()); mSaveEventLogger.maybeSetSaveUiShownReason( - SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE); + SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE); } if (saveInfo.getOptionalIds() != null) { Collections.addAll(trackedViews, saveInfo.getOptionalIds()); mSaveEventLogger.maybeSetSaveUiShownReason( - SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE); + SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE); } } if ((flags & SaveInfo.FLAG_DONT_SAVE_ON_FINISH) != 0) { - mSaveEventLogger.maybeSetSaveUiShownReason( - SAVE_UI_SHOWN_REASON_UNKNOWN); + mSaveEventLogger.maybeSetSaveUiShownReason(SAVE_UI_SHOWN_REASON_UNKNOWN); mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG); + NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG); saveOnFinish = false; } } else { flags = 0; - mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_NO_SAVE_INFO); + mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO); saveTriggerId = null; } @@ -5427,21 +5990,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { if (sVerbose) { - Slog.v(TAG, "updateTrackedIdsLocked(): trackedViews: " + trackedViews - + " fillableIds: " + fillableIds + " triggerId: " + saveTriggerId - + " saveOnFinish:" + saveOnFinish + " flags: " + flags - + " hasSaveInfo: " + (saveInfo != null)); - } - mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible, - saveOnFinish, toArray(fillableIds), saveTriggerId, hasAuthentication); + Slog.v( + TAG, + "updateTrackedIdsLocked(): trackedViews: " + + trackedViews + + " fillableIds: " + + fillableIds + + " triggerId: " + + saveTriggerId + + " saveOnFinish:" + + saveOnFinish + + " flags: " + + flags + + " hasSaveInfo: " + + (saveInfo != null)); + } + mClient.setTrackedViews( + id, + toArray(trackedViews), + mSaveOnAllViewsInvisible, + saveOnFinish, + toArray(fillableIds), + saveTriggerId, + hasAuthentication); } catch (RemoteException e) { Slog.w(TAG, "Cannot set tracked ids", e); } } - /** - * Sets the state of views that failed to autofill. - */ + /** Sets the state of views that failed to autofill. */ @GuardedBy("mLock") void setAutofillFailureLocked(@NonNull List<AutofillId> ids, boolean isRefill) { if (sVerbose && !ids.isEmpty()) { @@ -5464,9 +6041,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeSetViewFillFailureCounts(ids, isRefill); } - /** - * Sets the state of views that failed to autofill. - */ + /** Sets the state of views that failed to autofill. */ @GuardedBy("mLock") void setViewAutofilledLocked(@NonNull AutofillId id) { if (sVerbose) { @@ -5478,17 +6053,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeAddSuccessId(id); } - /** - * Sets the state of views that failed to autofill. - */ + /** Sets the state of views that failed to autofill. */ void setNotifyNotExpiringResponseDuringAuth() { synchronized (mLock) { mPresentationStatsEventLogger.maybeSetNotifyNotExpiringResponseDuringAuth(); } } - /** - * Sets the state of views that failed to autofill. - */ + + /** Sets the state of views that failed to autofill. */ void setLogViewEnteredIgnoredDuringAuth() { synchronized (mLock) { mPresentationStatsEventLogger.notifyViewEnteredIgnoredDuringAuthCount(); @@ -5496,10 +6068,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void replaceResponseLocked(@NonNull FillResponse oldResponse, - @NonNull FillResponse newResponse, @Nullable Bundle newClientState) { + private void replaceResponseLocked( + @NonNull FillResponse oldResponse, + @NonNull FillResponse newResponse, + @Nullable Bundle newClientState) { // Disassociate view states with the old response - setViewStatesLocked(oldResponse, ViewState.STATE_INITIAL, /* clearResponse= */ true, + setViewStatesLocked( + oldResponse, + ViewState.STATE_INITIAL, + /* clearResponse= */ true, /* isPrimary= */ true); // Move over the id newResponse.setRequestId(oldResponse.getRequestId()); @@ -5519,7 +6096,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<AutofillId> autofillableIds; if (context != null) { final AssistStructure structure = context.getStructure(); - autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */true); + autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */ true); } else { Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId); autofillableIds = null; @@ -5535,8 +6112,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags); if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) { if (sVerbose) { - Slog.v(TAG, "canceling session " + id + " when service returned null and it cannot " - + "be augmented. AutofillableIds: " + autofillableIds); + Slog.v( + TAG, + "canceling session " + + id + + " when service returned null and it cannot " + + "be augmented. AutofillableIds: " + + autofillableIds); } // Nothing to be done, but need to notify client. notifyUnavailableToClient(AutofillManager.STATE_FINISHED, autofillableIds); @@ -5544,15 +6126,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } else { if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) { if (sVerbose) { - Slog.v(TAG, "keeping session " + id + " when service returned null and " - + "augmented service is disabled for password fields. " - + "AutofillableIds: " + autofillableIds); + Slog.v( + TAG, + "keeping session " + + id + + " when service returned null and " + + "augmented service is disabled for password fields. " + + "AutofillableIds: " + + autofillableIds); } mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); } else { if (sVerbose) { - Slog.v(TAG, "keeping session " + id + " when service returned null but " - + "it can be augmented. AutofillableIds: " + autofillableIds); + Slog.v( + TAG, + "keeping session " + + id + + " when service returned null but " + + "it can be augmented. AutofillableIds: " + + autofillableIds); } } mAugmentedAutofillableIds = autofillableIds; @@ -5567,8 +6159,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Tries to trigger Augmented Autofill when the standard service could not fulfill a request. * - * <p> The request may not have been sent when this method returns as it may be waiting for - * the inline suggestion request asynchronously. + * <p>The request may not have been sent when this method returns as it may be waiting for the + * inline suggestion request asynchronously. * * @return callback to destroy the autofill UI, or {@code null} if not supported. */ @@ -5582,8 +6174,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Check if Smart Suggestions is supported... - final @SmartSuggestionMode int supportedModes = mService - .getSupportedSmartSuggestionModesLocked(); + final @SmartSuggestionMode int supportedModes = + mService.getSupportedSmartSuggestionModesLocked(); if (supportedModes == 0) { if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no supported modes"); return null; @@ -5591,8 +6183,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // ...then if the service is set for the user - final RemoteAugmentedAutofillService remoteService = mService - .getRemoteAugmentedAutofillServiceLocked(); + final RemoteAugmentedAutofillService remoteService = + mService.getRemoteAugmentedAutofillServiceLocked(); if (remoteService == null) { if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no service for user"); return null; @@ -5612,25 +6204,37 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - final boolean isAllowlisted = mService - .isWhitelistedForAugmentedAutofillLocked(mComponentName); + final boolean isAllowlisted = + mService.isWhitelistedForAugmentedAutofillLocked(mComponentName); if (!isAllowlisted) { if (sVerbose) { - Slog.v(TAG, "triggerAugmentedAutofillLocked(): " - + ComponentName.flattenToShortString(mComponentName) + " not whitelisted "); - } - logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(), - mCurrentViewId, isAllowlisted, /* isInline= */ null); + Slog.v( + TAG, + "triggerAugmentedAutofillLocked(): " + + ComponentName.flattenToShortString(mComponentName) + + " not whitelisted "); + } + logAugmentedAutofillRequestLocked( + mode, + remoteService.getComponentName(), + mCurrentViewId, + isAllowlisted, + /* isInline= */ null); return null; } if (sVerbose) { - Slog.v(TAG, "calling Augmented Autofill Service (" - + ComponentName.flattenToShortString(remoteService.getComponentName()) - + ") on view " + mCurrentViewId + " using suggestion mode " - + getSmartSuggestionModeToString(mode) - + " when server returned null for session " + this.id); + Slog.v( + TAG, + "calling Augmented Autofill Service (" + + ComponentName.flattenToShortString(remoteService.getComponentName()) + + ") on view " + + mCurrentViewId + + " using suggestion mode " + + getSmartSuggestionModeToString(mode) + + " when server returned null for session " + + this.id); } // Log FillRequest for Augmented Autofill. mFillRequestEventLogger.startLogForNewRequest(); @@ -5648,8 +6252,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mAugmentedRequestsLogs == null) { mAugmentedRequestsLogs = new ArrayList<>(); } - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_AUGMENTED_REQUEST, - remoteService.getComponentName().getPackageName()); + final LogMaker log = + newLogMaker( + MetricsEvent.AUTOFILL_AUGMENTED_REQUEST, + remoteService.getComponentName().getPackageName()); mAugmentedRequestsLogs.add(log); final AutofillId focusedId = mCurrentViewId; @@ -5715,8 +6321,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } synchronized (session.mLock) { session.mInlineSessionController.onCreateInlineSuggestionsRequestLocked( - mFocusedId, /*requestConsumer=*/ mRequestAugmentedAutofill, - result); + mFocusedId, /* requestConsumer= */ mRequestAugmentedAutofill, result); } } } @@ -5741,19 +6346,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mIsAllowlisted = isAllowlisted; mMode = mode; mCurrentValue = currentValue; - } + @Override public void accept(InlineSuggestionsRequest inlineSuggestionsRequest) { Session session = mSessionWeakRef.get(); - if (logIfSessionNull( - session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) { + if (logIfSessionNull(session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) { return; } session.onAugmentedAutofillInlineSuggestionAccept( inlineSuggestionsRequest, mFocusedId, mIsAllowlisted, mMode, mCurrentValue); - } } @@ -5770,8 +6373,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public Boolean apply(InlineFillUi inlineFillUi) { Session session = mSessionWeakRef.get(); - if (logIfSessionNull( - session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) { + if (logIfSessionNull(session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) { return false; } @@ -5801,8 +6403,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * If the session is null or has been destroyed, log the error msg, and return true. - * This is a helper function intended to be called when de-referencing from a weak reference. + * If the session is null or has been destroyed, log the error msg, and return true. This is a + * helper function intended to be called when de-referencing from a weak reference. + * * @param session * @param logPrefix * @return true if the session is null, false otherwise. @@ -5830,15 +6433,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { final RemoteAugmentedAutofillService remoteService = mService.getRemoteAugmentedAutofillServiceLocked(); - logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(), - focussedId, isAllowlisted, inlineSuggestionsRequest != null); - remoteService.onRequestAutofillLocked(id, mClient, - taskId, mComponentName, mActivityToken, - AutofillId.withoutSession(focussedId), currentValue, + logAugmentedAutofillRequestLocked( + mode, + remoteService.getComponentName(), + focussedId, + isAllowlisted, + inlineSuggestionsRequest != null); + remoteService.onRequestAutofillLocked( + id, + mClient, + taskId, + mComponentName, + mActivityToken, + AutofillId.withoutSession(focussedId), + currentValue, inlineSuggestionsRequest, new AugmentedAutofillInlineSuggestionsResponseCallback(this), new AugmentedAutofillErrorCallback(this), - mService.getRemoteInlineSuggestionRenderServiceLocked(), userId); + mService.getRemoteInlineSuggestionRenderServiceLocked(), + userId); } } @@ -5847,15 +6460,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState cancelAugmentedAutofillLocked(); // Also cancel augmented in IME - mInlineSessionController.setInlineFillUiLocked( - InlineFillUi.emptyUi(mCurrentViewId)); + mInlineSessionController.setInlineFillUiLocked(InlineFillUi.emptyUi(mCurrentViewId)); } } @GuardedBy("mLock") private void cancelAugmentedAutofillLocked() { - final RemoteAugmentedAutofillService remoteService = mService - .getRemoteAugmentedAutofillServiceLocked(); + final RemoteAugmentedAutofillService remoteService = + mService.getRemoteAugmentedAutofillServiceLocked(); if (remoteService == null) { Slog.w(TAG, "cancelAugmentedAutofillLocked(): no service for user"); return; @@ -5865,8 +6477,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void processResponseLocked(@NonNull FillResponse newResponse, - @Nullable Bundle newClientState, int flags) { + private void processResponseLocked( + @NonNull FillResponse newResponse, @Nullable Bundle newClientState, int flags) { // Make sure we are hiding the UI which will be shown // only if handling the current response requires it. mUi.hideAll(this); @@ -5878,9 +6490,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int requestId = newResponse.getRequestId(); if (sVerbose) { - Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId - + ",flags=" + flags + ", reqId=" + requestId + ", resp=" + newResponse - + ",newClientState=" + newClientState); + Slog.v( + TAG, + "processResponseLocked(): mCurrentViewId=" + + mCurrentViewId + + ",flags=" + + flags + + ", reqId=" + + requestId + + ", resp=" + + newResponse + + ",newClientState=" + + newClientState); } if (mResponses == null) { @@ -5892,8 +6513,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mResponses.put(requestId, newResponse); mClientState = newClientState != null ? newClientState : newResponse.getClientState(); - boolean webviewRequestedCredman = newClientState != null && newClientState.getBoolean( - WEBVIEW_REQUESTED_CREDENTIAL_KEY, false); + boolean webviewRequestedCredman = + newClientState != null + && newClientState.getBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, false); List<Dataset> datasetList = newResponse.getDatasets(); mPresentationStatsEventLogger.maybeSetWebviewRequestedCredential(webviewRequestedCredman); @@ -5901,7 +6523,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId); mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList); - setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false, + setViewStatesLocked( + newResponse, + ViewState.STATE_FILLABLE, + /* clearResponse= */ false, /* isPrimary= */ true); updateFillDialogTriggerIdsLocked(); updateTrackedIdsLocked(); @@ -5914,12 +6539,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState currentView.maybeCallOnFillReady(flags); } - /** - * Sets the state of all views in the given response. - */ + /** Sets the state of all views in the given response. */ @GuardedBy("mLock") - private void setViewStatesLocked(FillResponse response, int state, boolean clearResponse, - boolean isPrimary) { + private void setViewStatesLocked( + FillResponse response, int state, boolean clearResponse, boolean isPrimary) { final List<Dataset> datasets = response.getDatasets(); if (datasets != null && !datasets.isEmpty()) { for (int i = 0; i < datasets.size(); i++) { @@ -5964,12 +6587,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Sets the state and response of all views in the given dataset. - */ + /** Sets the state and response of all views in the given dataset. */ @GuardedBy("mLock") - private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset, - int state, boolean clearResponse, boolean isPrimary) { + private void setViewStatesLocked( + @Nullable FillResponse response, + @NonNull Dataset dataset, + int state, + boolean clearResponse, + boolean isPrimary) { final ArrayList<AutofillId> ids = dataset.getFieldIds(); final ArrayList<AutofillValue> values = dataset.getFieldValues(); for (int j = 0; j < ids.size(); j++) { @@ -5989,10 +6614,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private ViewState createOrUpdateViewStateLocked(@NonNull AutofillId id, int state, - @Nullable AutofillValue value) { + private ViewState createOrUpdateViewStateLocked( + @NonNull AutofillId id, int state, @Nullable AutofillValue value) { ViewState viewState = mViewStates.get(id); - if (viewState != null) { + if (viewState != null) { viewState.setState(state); } else { viewState = new ViewState(id, this, state, mIsPrimaryCredential); @@ -6008,22 +6633,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return viewState; } - void autoFill(int requestId, int datasetIndex, Dataset dataset, boolean generateEvent, - int uiType) { + void autoFill( + int requestId, int datasetIndex, Dataset dataset, boolean generateEvent, int uiType) { if (sDebug) { - Slog.d(TAG, "autoFill(): requestId=" + requestId + "; datasetIdx=" + datasetIndex - + "; dataset=" + dataset); + Slog.d( + TAG, + "autoFill(): requestId=" + + requestId + + "; datasetIdx=" + + datasetIndex + + "; dataset=" + + dataset); } synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#autoFill() rejected - session: " - + id + " destroyed"); + Slog.w(TAG, "Call to Session#autoFill() rejected - session: " + id + " destroyed"); return; } // Selected dataset id is logged regardless of authentication result. mPresentationStatsEventLogger.maybeSetSelectedDatasetId(datasetIndex); mPresentationStatsEventLogger.maybeSetSelectedDatasetPickReason( - dataset.getEligibleReason()); + dataset.getEligibleReason()); // Autofill it directly... if (dataset.getAuthentication() == null) { if (generateEvent) { @@ -6039,10 +6669,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // ...or handle authentication. mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType); mPresentationStatsEventLogger.maybeSetAuthenticationType( - AUTHENTICATION_TYPE_DATASET_AUTHENTICATION); + AUTHENTICATION_TYPE_DATASET_AUTHENTICATION); // does not matter the value of isPrimary because null response won't be overridden. - setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, - /* clearResponse= */ false, /* isPrimary= */ true); + setViewStatesLocked( + null, + dataset, + ViewState.STATE_WAITING_DATASET_AUTH, + /* clearResponse= */ false, + /* isPrimary= */ true); final Intent fillInIntent; if (dataset.getCredentialFillInIntent() != null && Flags.autofillCredmanIntegration()) { Slog.d(TAG, "Setting credential fill intent"); @@ -6055,11 +6689,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState forceRemoveFromServiceLocked(); return; } - final int authenticationId = AutofillManager.makeAuthenticationId(requestId, - datasetIndex); - startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent, - /* authenticateInline= */false); - + final int authenticationId = + AutofillManager.makeAuthenticationId(requestId, datasetIndex); + startAuthentication( + authenticationId, + dataset.getAuthentication(), + fillInIntent, + /* authenticateInline= */ false); } } @@ -6072,13 +6708,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final FillContext context = getFillContextByRequestIdLocked(requestId); if (context == null) { - wtf(null, "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s", - requestId, mContexts); + wtf( + null, + "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s", + requestId, + mContexts); return null; } if (mLastInlineSuggestionsRequest != null && mLastInlineSuggestionsRequest.first == requestId) { - fillInIntent.putExtra(AutofillManager.EXTRA_INLINE_SUGGESTIONS_REQUEST, + fillInIntent.putExtra( + AutofillManager.EXTRA_INLINE_SUGGESTIONS_REQUEST, mLastInlineSuggestionsRequest.second); } fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure()); @@ -6121,12 +6761,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private void startAuthentication(int authenticationId, IntentSender intent, - Intent fillInIntent, boolean authenticateInline) { + private void startAuthentication( + int authenticationId, + IntentSender intent, + Intent fillInIntent, + boolean authenticateInline) { try { synchronized (mLock) { - mClient.authenticate(id, authenticationId, intent, fillInIntent, - authenticateInline); + mClient.authenticate( + id, authenticationId, intent, fillInIntent, authenticateInline); } } catch (RemoteException e) { Slog.e(TAG, "Error launching auth intent", e); @@ -6139,22 +6782,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * @hide */ static final class SaveResult { - /** - * Whether to record the save dialog has been shown. - */ + /** Whether to record the save dialog has been shown. */ private boolean mLogSaveShown; - /** - * Whether to remove the session. - */ + /** Whether to remove the session. */ private boolean mRemoveSession; - /** - * The reason why a save dialog was not shown. - */ + /** The reason why a save dialog was not shown. */ @NoSaveReason private int mSaveDialogNotShowReason; - SaveResult(boolean logSaveShown, boolean removeSession, + SaveResult( + boolean logSaveShown, + boolean removeSession, @NoSaveReason int saveDialogNotShowReason) { mLogSaveShown = logSaveShown; mRemoveSession = removeSession; @@ -6218,15 +6857,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public String toString() { - return "SaveResult: [logSaveShown=" + mLogSaveShown - + ", removeSession=" + mRemoveSession - + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]"; + return "SaveResult: [logSaveShown=" + + mLogSaveShown + + ", removeSession=" + + mRemoveSession + + ", saveDialogNotShowReason=" + + mSaveDialogNotShowReason + + "]"; } } /** - * Class maintaining the state of the requests to - * {@link android.service.assist.classification.FieldClassificationService}. + * Class maintaining the state of the requests to {@link + * android.service.assist.classification.FieldClassificationService}. */ private static final class ClassificationState { @@ -6234,18 +6877,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Initial state indicating that the request for classification hasn't been triggered yet. */ private static final int STATE_INITIAL = 1; - /** - * Assist request has been triggered, but awaiting response. - */ + + /** Assist request has been triggered, but awaiting response. */ private static final int STATE_PENDING_ASSIST_REQUEST = 2; - /** - * Classification request has been triggered, but awaiting response. - */ + + /** Classification request has been triggered, but awaiting response. */ private static final int STATE_PENDING_REQUEST = 3; - /** - * Classification response has been received. - */ + + /** Classification response has been received. */ private static final int STATE_RESPONSE = 4; + /** * Classification state has been invalidated, and the last response may no longer be valid. * This could occur due to various reasons like views changing their layouts, becoming @@ -6254,15 +6895,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ private static final int STATE_INVALIDATED = 5; - @IntDef(prefix = { "STATE_" }, value = { - STATE_INITIAL, - STATE_PENDING_ASSIST_REQUEST, - STATE_PENDING_REQUEST, - STATE_RESPONSE, - STATE_INVALIDATED - }) + @IntDef( + prefix = {"STATE_"}, + value = { + STATE_INITIAL, + STATE_PENDING_ASSIST_REQUEST, + STATE_PENDING_REQUEST, + STATE_RESPONSE, + STATE_INVALIDATED + }) @Retention(RetentionPolicy.SOURCE) - @interface ClassificationRequestState{} + @interface ClassificationRequestState {} @GuardedBy("mLock") private @ClassificationRequestState int mState = STATE_INITIAL; @@ -6284,8 +6927,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Typically, there would be a 1:1 mapping. However, in certain cases, we may have a hint - * being applicable to many types. An example of this being new/change password forms, - * where you need to confirm the passward twice. + * being applicable to many types. An example of this being new/change password forms, where + * you need to confirm the passward twice. */ @GuardedBy("mLock") private ArrayMap<String, Set<AutofillId>> mHintsToAutofillIdMap; @@ -6317,8 +6960,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Process the response received. + * * @return true if the response was processed, false otherwise. If there wasn't any - * response, yet this function was called, it would return false. + * response, yet this function was called, it would return false. */ @GuardedBy("mLock") private boolean processResponse() { @@ -6358,7 +7002,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private static void processDetections(Set<String> detections, AutofillId id, + private static void processDetections( + Set<String> detections, + AutofillId id, ArrayMap<String, Set<AutofillId>> currentMap) { for (String detection : detections) { Set<AutofillId> autofillIds; @@ -6416,37 +7062,67 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public String toString() { return "ClassificationState: [" - + "state=" + stateToString() - + ", mPendingFieldClassificationRequest=" + mPendingFieldClassificationRequest - + ", mLastFieldClassificationResponse=" + mLastFieldClassificationResponse - + ", mClassificationHintsMap=" + mClassificationHintsMap - + ", mClassificationGroupHintsMap=" + mClassificationGroupHintsMap - + ", mHintsToAutofillIdMap=" + mHintsToAutofillIdMap - + ", mGroupHintsToAutofillIdMap=" + mGroupHintsToAutofillIdMap + + "state=" + + stateToString() + + ", mPendingFieldClassificationRequest=" + + mPendingFieldClassificationRequest + + ", mLastFieldClassificationResponse=" + + mLastFieldClassificationResponse + + ", mClassificationHintsMap=" + + mClassificationHintsMap + + ", mClassificationGroupHintsMap=" + + mClassificationGroupHintsMap + + ", mHintsToAutofillIdMap=" + + mHintsToAutofillIdMap + + ", mGroupHintsToAutofillIdMap=" + + mGroupHintsToAutofillIdMap + "]"; } - } @Override public String toString() { - return "Session: [id=" + id + ", component=" + mComponentName - + ", state=" + sessionStateAsString(mSessionState) + "]"; + return "Session: [id=" + + id + + ", component=" + + mComponentName + + ", state=" + + sessionStateAsString(mSessionState) + + "]"; } @GuardedBy("mLock") void dumpLocked(String prefix, PrintWriter pw) { final String prefix2 = prefix + " "; - pw.print(prefix); pw.print("id: "); pw.println(id); - pw.print(prefix); pw.print("uid: "); pw.println(uid); - pw.print(prefix); pw.print("taskId: "); pw.println(taskId); - pw.print(prefix); pw.print("flags: "); pw.println(mFlags); - pw.print(prefix); pw.print("displayId: "); pw.println(mContext.getDisplayId()); - pw.print(prefix); pw.print("state: "); pw.println(sessionStateAsString(mSessionState)); - pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName); - pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); - pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime); - pw.print(prefix); pw.print("Time to show UI: "); + pw.print(prefix); + pw.print("id: "); + pw.println(id); + pw.print(prefix); + pw.print("uid: "); + pw.println(uid); + pw.print(prefix); + pw.print("taskId: "); + pw.println(taskId); + pw.print(prefix); + pw.print("flags: "); + pw.println(mFlags); + pw.print(prefix); + pw.print("displayId: "); + pw.println(mContext.getDisplayId()); + pw.print(prefix); + pw.print("state: "); + pw.println(sessionStateAsString(mSessionState)); + pw.print(prefix); + pw.print("mComponentName: "); + pw.println(mComponentName); + pw.print(prefix); + pw.print("mActivityToken: "); + pw.println(mActivityToken); + pw.print(prefix); + pw.print("mStartTime: "); + pw.println(mStartTime); + pw.print(prefix); + pw.print("Time to show UI: "); if (mUiShownTime == 0) { pw.println("N/A"); } else { @@ -6454,41 +7130,67 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.println(); } final int requestLogsSizes = mRequestLogs.size(); - pw.print(prefix); pw.print("mSessionLogs: "); pw.println(requestLogsSizes); + pw.print(prefix); + pw.print("mSessionLogs: "); + pw.println(requestLogsSizes); for (int i = 0; i < requestLogsSizes; i++) { final int requestId = mRequestLogs.keyAt(i); final LogMaker log = mRequestLogs.valueAt(i); - pw.print(prefix2); pw.print('#'); pw.print(i); pw.print(": req="); - pw.print(requestId); pw.print(", log=" ); dumpRequestLog(pw, log); pw.println(); + pw.print(prefix2); + pw.print('#'); + pw.print(i); + pw.print(": req="); + pw.print(requestId); + pw.print(", log="); + dumpRequestLog(pw, log); + pw.println(); } - pw.print(prefix); pw.print("mResponses: "); + pw.print(prefix); + pw.print("mResponses: "); if (mResponses == null) { pw.println("null"); } else { pw.println(mResponses.size()); for (int i = 0; i < mResponses.size(); i++) { - pw.print(prefix2); pw.print('#'); pw.print(i); - pw.print(' '); pw.println(mResponses.valueAt(i)); - } - } - pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId); - pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed); - pw.print(prefix); pw.print("mShowingSaveUi: "); pw.println(mSessionFlags.mShowingSaveUi); - pw.print(prefix); pw.print("mPendingSaveUi: "); pw.println(mPendingSaveUi); + pw.print(prefix2); + pw.print('#'); + pw.print(i); + pw.print(' '); + pw.println(mResponses.valueAt(i)); + } + } + pw.print(prefix); + pw.print("mCurrentViewId: "); + pw.println(mCurrentViewId); + pw.print(prefix); + pw.print("mDestroyed: "); + pw.println(mDestroyed); + pw.print(prefix); + pw.print("mShowingSaveUi: "); + pw.println(mSessionFlags.mShowingSaveUi); + pw.print(prefix); + pw.print("mPendingSaveUi: "); + pw.println(mPendingSaveUi); final int numberViews = mViewStates.size(); - pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size()); + pw.print(prefix); + pw.print("mViewStates size: "); + pw.println(mViewStates.size()); for (int i = 0; i < numberViews; i++) { - pw.print(prefix); pw.print("ViewState at #"); pw.println(i); + pw.print(prefix); + pw.print("ViewState at #"); + pw.println(i); mViewStates.valueAt(i).dump(prefix2, pw); } - pw.print(prefix); pw.print("mContexts: " ); + pw.print(prefix); + pw.print("mContexts: "); if (mContexts != null) { int numContexts = mContexts.size(); for (int i = 0; i < numContexts; i++) { FillContext context = mContexts.get(i); - pw.print(prefix2); pw.print(context); + pw.print(prefix2); + pw.print(context); if (sVerbose) { pw.println("AssistStructure dumped at logcat)"); @@ -6500,43 +7202,62 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.println("null"); } - pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback); + pw.print(prefix); + pw.print("mHasCallback: "); + pw.println(mHasCallback); if (mClientState != null) { - pw.print(prefix); pw.print("mClientState: "); pw.print(mClientState.getSize()); pw - .println(" bytes"); - } - pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode); - pw.print(prefix); pw.print("mUrlBar: "); + pw.print(prefix); + pw.print("mClientState: "); + pw.print(mClientState.getSize()); + pw.println(" bytes"); + } + pw.print(prefix); + pw.print("mCompatMode: "); + pw.println(mCompatMode); + pw.print(prefix); + pw.print("mUrlBar: "); if (mUrlBar == null) { pw.println("N/A"); } else { - pw.print("id="); pw.print(mUrlBar.getAutofillId()); - pw.print(" domain="); pw.print(mUrlBar.getWebDomain()); - pw.print(" text="); Helper.printlnRedactedText(pw, mUrlBar.getText()); - } - pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println( - mSaveOnAllViewsInvisible); - pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds); + pw.print("id="); + pw.print(mUrlBar.getAutofillId()); + pw.print(" domain="); + pw.print(mUrlBar.getWebDomain()); + pw.print(" text="); + Helper.printlnRedactedText(pw, mUrlBar.getText()); + } + pw.print(prefix); + pw.print("mSaveOnAllViewsInvisible: "); + pw.println(mSaveOnAllViewsInvisible); + pw.print(prefix); + pw.print("mSelectedDatasetIds: "); + pw.println(mSelectedDatasetIds); if (mSessionFlags.mAugmentedAutofillOnly) { - pw.print(prefix); pw.println("For Augmented Autofill Only"); + pw.print(prefix); + pw.println("For Augmented Autofill Only"); } if (mSessionFlags.mFillDialogDisabled) { - pw.print(prefix); pw.println("Fill Dialog disabled"); + pw.print(prefix); + pw.println("Fill Dialog disabled"); } if (mLastFillDialogTriggerIds != null) { - pw.print(prefix); pw.println("Last Fill Dialog trigger ids: "); + pw.print(prefix); + pw.println("Last Fill Dialog trigger ids: "); pw.println(mSelectedDatasetIds); } if (mAugmentedAutofillDestroyer != null) { - pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer"); + pw.print(prefix); + pw.println("has mAugmentedAutofillDestroyer"); } if (mAugmentedRequestsLogs != null) { - pw.print(prefix); pw.print("number augmented requests: "); + pw.print(prefix); + pw.print("number augmented requests: "); pw.println(mAugmentedRequestsLogs.size()); } if (mAugmentedAutofillableIds != null) { - pw.print(prefix); pw.print("mAugmentedAutofillableIds: "); + pw.print(prefix); + pw.print("mAugmentedAutofillableIds: "); pw.println(mAugmentedAutofillableIds); } if (mRemoteFillService != null) { @@ -6545,21 +7266,32 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } private static void dumpRequestLog(@NonNull PrintWriter pw, @NonNull LogMaker log) { - pw.print("CAT="); pw.print(log.getCategory()); + pw.print("CAT="); + pw.print(log.getCategory()); pw.print(", TYPE="); final int type = log.getType(); switch (type) { - case MetricsEvent.TYPE_SUCCESS: pw.print("SUCCESS"); break; - case MetricsEvent.TYPE_FAILURE: pw.print("FAILURE"); break; - case MetricsEvent.TYPE_CLOSE: pw.print("CLOSE"); break; - default: pw.print("UNSUPPORTED"); - } - pw.print('('); pw.print(type); pw.print(')'); - pw.print(", PKG="); pw.print(log.getPackageName()); - pw.print(", SERVICE="); pw.print(log - .getTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE)); - pw.print(", ORDINAL="); pw.print(log - .getTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL)); + case MetricsEvent.TYPE_SUCCESS: + pw.print("SUCCESS"); + break; + case MetricsEvent.TYPE_FAILURE: + pw.print("FAILURE"); + break; + case MetricsEvent.TYPE_CLOSE: + pw.print("CLOSE"); + break; + default: + pw.print("UNSUPPORTED"); + } + pw.print('('); + pw.print(type); + pw.print(')'); + pw.print(", PKG="); + pw.print(log.getPackageName()); + pw.print(", SERVICE="); + pw.print(log.getTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE)); + pw.print(", ORDINAL="); + pw.print(log.getTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL)); dumpNumericValue(pw, log, "FLAGS", MetricsEvent.FIELD_AUTOFILL_FLAGS); dumpNumericValue(pw, log, "NUM_DATASETS", MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS); dumpNumericValue(pw, log, "UI_LATENCY", MetricsEvent.FIELD_AUTOFILL_DURATION); @@ -6569,64 +7301,86 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.print(", AUTH_STATUS="); switch (authStatus) { case MetricsEvent.AUTOFILL_AUTHENTICATED: - pw.print("AUTHENTICATED"); break; + pw.print("AUTHENTICATED"); + break; case MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED: - pw.print("DATASET_AUTHENTICATED"); break; + pw.print("DATASET_AUTHENTICATED"); + break; case MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION: - pw.print("INVALID_AUTHENTICATION"); break; + pw.print("INVALID_AUTHENTICATION"); + break; case MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION: - pw.print("INVALID_DATASET_AUTHENTICATION"); break; - default: pw.print("UNSUPPORTED"); + pw.print("INVALID_DATASET_AUTHENTICATION"); + break; + default: + pw.print("UNSUPPORTED"); } - pw.print('('); pw.print(authStatus); pw.print(')'); + pw.print('('); + pw.print(authStatus); + pw.print(')'); } - dumpNumericValue(pw, log, "FC_IDS", - MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS); - dumpNumericValue(pw, log, "COMPAT_MODE", - MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE); + dumpNumericValue( + pw, log, "FC_IDS", MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS); + dumpNumericValue(pw, log, "COMPAT_MODE", MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE); } - private static void dumpNumericValue(@NonNull PrintWriter pw, @NonNull LogMaker log, - @NonNull String field, int tag) { + private static void dumpNumericValue( + @NonNull PrintWriter pw, @NonNull LogMaker log, @NonNull String field, int tag) { final int value = getNumericValue(log, tag); if (value != 0) { - pw.print(", "); pw.print(field); pw.print('='); pw.print(value); + pw.print(", "); + pw.print(field); + pw.print('='); + pw.print(value); } } - void sendCredentialManagerResponseToApp(@Nullable GetCredentialResponse response, - @Nullable GetCredentialException exception, @NonNull AutofillId viewId) { + void sendCredentialManagerResponseToApp( + @Nullable GetCredentialResponse response, + @Nullable GetCredentialException exception, + @NonNull AutofillId viewId) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#sendCredentialManagerResponseToApp() rejected " - + "- session: " + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#sendCredentialManagerResponseToApp() rejected " + + "- session: " + + id + + " destroyed"); return; } try { final ViewState viewState = mViewStates.get(viewId); if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly() - && viewState != null && viewState.id.getSessionId() != id) { + && viewState != null + && viewState.id.getSessionId() != id) { if (sVerbose) { - Slog.v(TAG, "Skipping sending credential response to view: " - + viewId + " as it isn't part of the current session: " + id); + Slog.v( + TAG, + "Skipping sending credential response to view: " + + viewId + + " as it isn't part of the current session: " + + id); } } if (exception != null) { if (viewId.isVirtualInt()) { - sendResponseToViewNode(viewId, /*response=*/ null, exception); + sendResponseToViewNode(viewId, /* response= */ null, exception); } else { - mClient.onGetCredentialException(id, viewId, exception.getType(), - exception.getMessage()); + mClient.onGetCredentialException( + id, viewId, exception.getType(), exception.getMessage()); } } else if (response != null) { if (viewId.isVirtualInt()) { - sendResponseToViewNode(viewId, response, /*exception=*/ null); + sendResponseToViewNode(viewId, response, /* exception= */ null); } else { mClient.onGetCredentialResponse(id, viewId, response); } } else { - Slog.w(TAG, "sendCredentialManagerResponseToApp called with null response" - + "and exception"); + Slog.w( + TAG, + "sendCredentialManagerResponseToApp called with null response" + + "and exception"); } } catch (RemoteException e) { Slog.w(TAG, "Error sending credential response to activity: " + e); @@ -6635,23 +7389,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void sendResponseToViewNode(AutofillId viewId, GetCredentialResponse response, - GetCredentialException exception) { + private void sendResponseToViewNode( + AutofillId viewId, GetCredentialResponse response, GetCredentialException exception) { ViewNode viewNode = getViewNodeFromContextsLocked(viewId); if (viewNode != null && viewNode.getPendingCredentialCallback() != null) { Bundle resultData = new Bundle(); if (response != null) { resultData.putParcelable( - CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, - response); - viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR, - resultData); + CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, response); + viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR, resultData); } else if (exception != null) { resultData.putStringArray( CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, new String[] {exception.getType(), exception.getMessage()}); - viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR, - resultData); + viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR, resultData); } } else { Slog.w(TAG, "View node not found after GetCredentialResponse"); @@ -6661,8 +7412,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void autoFillApp(Dataset dataset) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#autoFillApp() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#autoFillApp() rejected - session: " + id + " destroyed"); return; } try { @@ -6671,8 +7423,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final List<AutofillId> ids = new ArrayList<>(entryCount); final List<AutofillValue> values = new ArrayList<>(entryCount); boolean waitingDatasetAuth = false; - boolean hideHighlight = (entryCount == 1 - && dataset.getFieldIds().get(0).equals(mCurrentViewId)); + boolean hideHighlight = + highlightAutofillSingleField() + ? false + : (entryCount == 1 + && dataset.getFieldIds().get(0).equals(mCurrentViewId)); // Count how many views are filtered because they are not in current session int numOfViewsFiltered = 0; for (int i = 0; i < entryCount; i++) { @@ -6682,10 +7437,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId viewId = dataset.getFieldIds().get(i); final ViewState viewState = mViewStates.get(viewId); if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly() - && viewState != null && viewState.id.getSessionId() != id) { + && viewState != null + && viewState.id.getSessionId() != id) { if (sVerbose) { - Slog.v(TAG, "Skipping filling view: " + - viewId + " as it isn't part of the current session: " + id); + Slog.v( + TAG, + "Skipping filling view: " + + viewId + + " as it isn't part of the current session: " + + id); } numOfViewsFiltered += 1; continue; @@ -6721,8 +7481,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // does not matter the value of isPrimary because null response won't be // overridden. - setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED, - /* clearResponse= */ false, /* isPrimary= */ true); + setViewStatesLocked( + null, + dataset, + ViewState.STATE_AUTOFILLED, + /* clearResponse= */ false, + /* isPrimary= */ true); } } catch (RemoteException e) { Slog.w(TAG, "Error autofilling activity: " + e); @@ -6750,7 +7514,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionCommittedEventLogger.maybeSetCommitReason(val); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); mSessionCommittedEventLogger.maybeSetSessionDurationMillis( - SystemClock.elapsedRealtime() - mStartTime); + SystemClock.elapsedRealtime() - mStartTime); mFillRequestEventLogger.logAndEndEvent(); mFillResponseEventLogger.logAndEndEvent(); mPresentationStatsEventLogger.logAndEndEvent("log all events"); @@ -6808,8 +7572,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - final int totalAugmentedRequests = mAugmentedRequestsLogs == null ? 0 - : mAugmentedRequestsLogs.size(); + final int totalAugmentedRequests = + mAugmentedRequestsLogs == null ? 0 : mAugmentedRequestsLogs.size(); if (totalAugmentedRequests > 0) { if (sVerbose) { Slog.v(TAG, "destroyLocked(): logging " + totalRequests + " augmented requests"); @@ -6820,11 +7584,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests); + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests); if (totalAugmentedRequests > 0) { - log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS, - totalAugmentedRequests); + log.addTaggedData( + MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS, totalAugmentedRequests); } if (mSessionFlags.mAugmentedAutofillOnly) { log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_AUGMENTED_ONLY, 1); @@ -6846,8 +7611,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void forceRemoveFromServiceIfForAugmentedOnlyLocked() { if (sVerbose) { - Slog.v(TAG, "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + this.id + "): " - + mSessionFlags.mAugmentedAutofillOnly); + Slog.v( + TAG, + "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + + this.id + + "): " + + mSessionFlags.mAugmentedAutofillOnly); } if (!mSessionFlags.mAugmentedAutofillOnly) return; @@ -6880,9 +7649,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Thread-safe version of {@link #removeFromServiceLocked()}. - */ + /** Thread-safe version of {@link #removeFromServiceLocked()}. */ private void removeFromService() { synchronized (mLock) { removeFromServiceLocked(); @@ -6897,8 +7664,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void removeFromServiceLocked() { if (sVerbose) Slog.v(TAG, "removeFromServiceLocked(" + this.id + "): " + mPendingSaveUi); if (mDestroyed) { - Slog.w(TAG, "Call to Session#removeFromServiceLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#removeFromServiceLocked() rejected - session: " + + id + + " destroyed"); return; } if (isSaveUiPendingLocked()) { @@ -6922,18 +7692,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Checks whether this session is hiding the Save UI to handle a custom description link for - * a specific {@code token} created by - * {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}. + * Checks whether this session is hiding the Save UI to handle a custom description link for a + * specific {@code token} created by {@link PendingUi#PendingUi(IBinder, int, + * IAutoFillManagerClient)}. */ @GuardedBy("mLock") boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) { return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken()); } - /** - * Checks whether this session is hiding the Save UI to handle a custom description link. - */ + /** Checks whether this session is hiding the Save UI to handle a custom description link. */ @GuardedBy("mLock") private boolean isSaveUiPendingLocked() { return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING; @@ -6942,8 +7710,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Return latest response index in mResponses SparseArray. @GuardedBy("mLock") private int getLastResponseIndexLocked() { - if (mResponses == null || mResponses.size() == 0) { - return -1; + if (mResponses == null || mResponses.size() == 0) { + return -1; } List<Integer> requestIdList = new ArrayList<>(); final int responseCount = mResponses.size(); @@ -6967,15 +7735,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void logAuthenticationStatusLocked(int requestId, int status) { - addTaggedDataToRequestLogLocked(requestId, - MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status); + addTaggedDataToRequestLogLocked( + requestId, MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status); } @GuardedBy("mLock") private void addTaggedDataToRequestLogLocked(int requestId, int tag, @Nullable Object value) { final LogMaker requestLog = mRequestLogs.get(requestId); if (requestLog == null) { - Slog.w(TAG, + Slog.w( + TAG, "addTaggedDataToRequestLogLocked(tag=" + tag + "): no log for id " + requestId); return; } @@ -6983,20 +7752,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void logAugmentedAutofillRequestLocked(int mode, - ComponentName augmentedRemoteServiceName, AutofillId focusedId, boolean isWhitelisted, + private void logAugmentedAutofillRequestLocked( + int mode, + ComponentName augmentedRemoteServiceName, + AutofillId focusedId, + boolean isWhitelisted, Boolean isInline) { final String historyItem = - "aug:id=" + id + " u=" + uid + " m=" + mode - + " a=" + ComponentName.flattenToShortString(mComponentName) - + " f=" + focusedId - + " s=" + augmentedRemoteServiceName - + " w=" + isWhitelisted - + " i=" + isInline; + "aug:id=" + + id + + " u=" + + uid + + " m=" + + mode + + " a=" + + ComponentName.flattenToShortString(mComponentName) + + " f=" + + focusedId + + " s=" + + augmentedRemoteServiceName + + " w=" + + isWhitelisted + + " i=" + + isInline; mService.getMaster().logRequestLocked(historyItem); } - private void wtf(@Nullable Exception e, String fmt, Object...args) { + private void wtf(@Nullable Exception e, String fmt, Object... args) { final String message = String.format(fmt, args); synchronized (mLock) { mWtfHistory.log(message); @@ -7051,13 +7833,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mClassificationState.updateResponseReceived(response); } - public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) { + public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) {} - } - - public void onClassificationRequestTimeout(int requestId) { - - } + public void onClassificationRequestTimeout(int requestId) {} @Override public void onServiceDied(@NonNull RemoteFieldClassificationService service) { @@ -7070,12 +7848,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void logFieldClassificationEvent( - long startTime, FieldClassificationResponse response, + long startTime, + FieldClassificationResponse response, @FieldClassificationEventLogger.FieldClassificationStatus int status) { final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger(); logger.startNewLogForRequest(); - logger.maybeSetLatencyMillis( - SystemClock.elapsedRealtime() - startTime); + logger.maybeSetLatencyMillis(SystemClock.elapsedRealtime() - startTime); logger.maybeSetAppPackageUid(uid); logger.maybeSetNextFillRequestId(mFillRequestIdSnapshot + 1); logger.maybeSetRequestId(sIdCounterForPcc.get()); diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java index 91ba9b3749fd..74908a4613be 100644 --- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java +++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java @@ -39,7 +39,9 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; @@ -59,9 +61,12 @@ public class CompanionTransportManager { @GuardedBy("mTransportsListeners") private final RemoteCallbackList<IOnTransportsChangedListener> mTransportsListeners = new RemoteCallbackList<>(); + /** Message type -> IOnMessageReceivedListener */ + @GuardedBy("mMessageListeners") @NonNull - private final SparseArray<IOnMessageReceivedListener> mMessageListeners = new SparseArray<>(); + private final SparseArray<Set<IOnMessageReceivedListener>> mMessageListeners = + new SparseArray<>(); public CompanionTransportManager(Context context, AssociationStore associationStore) { mContext = context; @@ -72,7 +77,12 @@ public class CompanionTransportManager { * Add a listener to receive callbacks when a message is received for the message type */ public void addListener(int message, @NonNull IOnMessageReceivedListener listener) { - mMessageListeners.put(message, listener); + synchronized (mMessageListeners) { + if (!mMessageListeners.contains(message)) { + mMessageListeners.put(message, new HashSet<IOnMessageReceivedListener>()); + } + mMessageListeners.get(message).add(listener); + } synchronized (mTransports) { for (int i = 0; i < mTransports.size(); i++) { mTransports.valueAt(i).addListener(message, listener); @@ -113,7 +123,12 @@ public class CompanionTransportManager { * Remove the listener to stop receiving calbacks when a message is received for the given type */ public void removeListener(int messageType, IOnMessageReceivedListener listener) { - mMessageListeners.remove(messageType); + synchronized (mMessageListeners) { + if (!mMessageListeners.contains(messageType)) { + return; + } + mMessageListeners.get(messageType).remove(listener); + } } /** @@ -315,8 +330,12 @@ public class CompanionTransportManager { } private void addMessageListenersToTransport(Transport transport) { - for (int i = 0; i < mMessageListeners.size(); i++) { - transport.addListener(mMessageListeners.keyAt(i), mMessageListeners.valueAt(i)); + synchronized (mMessageListeners) { + for (int i = 0; i < mMessageListeners.size(); i++) { + for (IOnMessageReceivedListener listener : mMessageListeners.valueAt(i)) { + transport.addListener(mMessageListeners.keyAt(i), listener); + } + } } } diff --git a/services/companion/java/com/android/server/companion/transport/Transport.java b/services/companion/java/com/android/server/companion/transport/Transport.java index 8a5774e55ce2..986bd6c91e17 100644 --- a/services/companion/java/com/android/server/companion/transport/Transport.java +++ b/services/companion/java/com/android/server/companion/transport/Transport.java @@ -40,8 +40,8 @@ import libcore.util.EmptyArray; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; @@ -71,7 +71,8 @@ public abstract class Transport { * the future to allow multiple listeners to receive callbacks for the same message type, the * value of the map can be a list. */ - private final Map<Integer, IOnMessageReceivedListener> mListeners; + @GuardedBy("mListeners") + private final SparseArray<Set<IOnMessageReceivedListener>> mListeners = new SparseArray<>(); private OnTransportClosedListener mOnTransportClosed; @@ -98,7 +99,6 @@ public abstract class Transport { mRemoteIn = new ParcelFileDescriptor.AutoCloseInputStream(fd); mRemoteOut = new ParcelFileDescriptor.AutoCloseOutputStream(fd); mContext = context; - mListeners = new HashMap<>(); } /** @@ -107,7 +107,12 @@ public abstract class Transport { * @param listener Execute when a message with the type is received */ public void addListener(int message, IOnMessageReceivedListener listener) { - mListeners.put(message, listener); + synchronized (mListeners) { + if (!mListeners.contains(message)) { + mListeners.put(message, new HashSet<IOnMessageReceivedListener>()); + } + mListeners.get(message).add(listener); + } } public int getAssociationId() { @@ -281,12 +286,19 @@ public abstract class Transport { } private void callback(int message, byte[] data) { - if (mListeners.containsKey(message)) { + Set<IOnMessageReceivedListener> listenersToCall; + synchronized (mListeners) { + if (!mListeners.contains(message)) { + return; + } + listenersToCall = mListeners.get(message); + } + Slog.d(TAG, "Message 0x" + Integer.toHexString(message) + + " is received from associationId " + mAssociationId + + ", sending data length " + data.length + " to the listener(s)."); + for (IOnMessageReceivedListener listener: listenersToCall) { try { - mListeners.get(message).onMessageReceived(getAssociationId(), data); - Slog.d(TAG, "Message 0x" + Integer.toHexString(message) - + " is received from associationId " + mAssociationId - + ", sending data length " + data.length + " to the listener."); + listener.onMessageReceived(getAssociationId(), data); } catch (RemoteException ignored) { } } diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index 504137a29977..6a1e319b4039 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -46,6 +46,7 @@ import android.util.TimingsTraceLog; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.pm.RoSystemFeatures; import com.android.internal.util.XmlUtils; import com.android.modules.utils.build.UnboundedSdkLevel; import com.android.server.pm.permission.PermissionAllowlist; @@ -212,6 +213,30 @@ public class SystemConfig { } } + /** + * Utility class for testing interaction with compile-time defined system features. + * @hide + */ + @VisibleForTesting + public static class Injector { + /** Whether a system feature is defined as enabled and available at compile-time. */ + public boolean isReadOnlySystemEnabledFeature(String featureName, int version) { + return Boolean.TRUE.equals(RoSystemFeatures.maybeHasFeature(featureName, version)); + } + + /** Whether a system feature is defined as disabled and unavailable at compile-time. */ + public boolean isReadOnlySystemDisabledFeature(String featureName, int version) { + return Boolean.FALSE.equals(RoSystemFeatures.maybeHasFeature(featureName, version)); + } + + /** The full set of system features defined as compile-time enabled and available. */ + public ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() { + return RoSystemFeatures.getReadOnlySystemEnabledFeatures(); + } + } + + private final Injector mInjector; + // These are the built-in shared libraries that were read from the // system configuration files. Keys are the library names; values are // the individual entries that contain information such as filename @@ -220,7 +245,7 @@ public class SystemConfig { // These are the features this devices supports that were read from the // system configuration files. - final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>(); + final ArrayMap<String, FeatureInfo> mAvailableFeatures; // These are the features which this device doesn't support; the OEM // partition uses these to opt-out of features from the system image. @@ -602,12 +627,26 @@ public class SystemConfig { public ArrayMap<String, Integer> getOemDefinedUids() { return mOemDefinedUids; } + /** * Only use for testing. Do NOT use in production code. * @param readPermissions false to create an empty SystemConfig; true to read the permissions. */ @VisibleForTesting public SystemConfig(boolean readPermissions) { + this(readPermissions, new Injector()); + } + + /** + * Only use for testing. Do NOT use in production code. + * @param readPermissions false to create an empty SystemConfig; true to read the permissions. + * @param injector Additional dependency injection for testing. + */ + @VisibleForTesting + public SystemConfig(boolean readPermissions, Injector injector) { + mInjector = injector; + mAvailableFeatures = mInjector.getReadOnlySystemEnabledFeatures(); + if (readPermissions) { Slog.w(TAG, "Constructing a test SystemConfig"); readAllPermissions(); @@ -617,6 +656,9 @@ public class SystemConfig { } SystemConfig() { + mInjector = new Injector(); + mAvailableFeatures = mInjector.getReadOnlySystemEnabledFeatures(); + TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER); log.traceBegin("readAllPermissions"); try { @@ -1775,6 +1817,10 @@ public class SystemConfig { } private void addFeature(String name, int version) { + if (mInjector.isReadOnlySystemDisabledFeature(name, version)) { + Slog.w(TAG, "Skipping feature addition for compile-time disabled feature: " + name); + return; + } FeatureInfo fi = mAvailableFeatures.get(name); if (fi == null) { fi = new FeatureInfo(); @@ -1787,6 +1833,10 @@ public class SystemConfig { } private void removeFeature(String name) { + if (mInjector.isReadOnlySystemEnabledFeature(name, /*version=*/0)) { + Slog.w(TAG, "Skipping feature removal for compile-time enabled feature: " + name); + return; + } if (mAvailableFeatures.remove(name) != null) { Slog.d(TAG, "Removed unavailable feature " + name); } diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index f8857d3e152a..d13dd2f2e1fc 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -443,6 +443,11 @@ final class UiModeManagerService extends SystemService { mDreamsDisabledByAmbientModeSuppression = disabledByAmbientModeSuppression; } + @VisibleForTesting + void setCurrentUser(int currentUserId) { + mCurrentUser = currentUserId; + } + @Override public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { mCurrentUser = to.getUserIdentifier(); @@ -864,9 +869,9 @@ final class UiModeManagerService extends SystemService { throw new IllegalArgumentException("Unknown mode: " + mode); } - final int user = UserHandle.getCallingUserId(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -929,7 +934,7 @@ final class UiModeManagerService extends SystemService { @AttentionModeThemeOverlayType int attentionModeThemeOverlayType) { setAttentionModeThemeOverlay_enforcePermission(); - enforceCurrentUserIfVisibleBackgroundEnabled(UserHandle.getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); synchronized (mLock) { if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) { @@ -1020,16 +1025,16 @@ final class UiModeManagerService extends SystemService { return false; } final int user = Binder.getCallingUserHandle().getIdentifier(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); - if (user != mCurrentUser && getContext().checkCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) { Slog.e(TAG, "Target user is not current user," + " INTERACT_ACROSS_USERS permission is required"); return false; - } + + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + // Store the last requested bedtime night mode state so that we don't need to notify // anyone if the user decides to switch to the night mode to bedtime. if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { @@ -1078,9 +1083,10 @@ final class UiModeManagerService extends SystemService { Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission"); return; } - final int user = UserHandle.getCallingUserId(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + + final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); @@ -1108,9 +1114,10 @@ final class UiModeManagerService extends SystemService { Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission"); return; } - final int user = UserHandle.getCallingUserId(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + + final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); @@ -1131,7 +1138,7 @@ final class UiModeManagerService extends SystemService { assertLegit(callingPackage); assertSingleProjectionType(projectionType); enforceProjectionTypePermissions(projectionType); - enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); synchronized (mLock) { if (mProjectionHolders == null) { @@ -1177,7 +1184,7 @@ final class UiModeManagerService extends SystemService { assertLegit(callingPackage); assertSingleProjectionType(projectionType); enforceProjectionTypePermissions(projectionType); - enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); return releaseProjectionUnchecked(projectionType, callingPackage); } @@ -1219,7 +1226,7 @@ final class UiModeManagerService extends SystemService { return; } - enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); synchronized (mLock) { if (mProjectionListeners == null) { diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index f5a297bfd4f5..65a2c187f1c8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -246,7 +246,7 @@ final class ActivityManagerConstants extends ContentObserver { static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000; - private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false; + private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = Flags.oomadjusterCachedAppTiers(); private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000; /** diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7f1d912d9a79..6405ebbb3dd5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -134,7 +134,6 @@ import static android.security.Flags.preventIntentRedirect; import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS; import static android.view.Display.INVALID_DISPLAY; -import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_CONFIGURATION; import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED; import static com.android.sdksandbox.flags.Flags.sdkSandboxInstrumentationInfo; import static com.android.server.am.ActiveServices.FGS_SAW_RESTRICTIONS; @@ -415,7 +414,6 @@ import com.android.internal.os.TransferPipe; import com.android.internal.os.Zygote; import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.policy.AttributeCache; -import com.android.internal.protolog.ProtoLog; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; @@ -4654,8 +4652,6 @@ public class ActivityManagerService extends IActivityManager.Stub notifyPackageUse(instr.mClass.getPackageName(), PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION); } - ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s", - processName, app.getWindowProcessController().getConfiguration()); ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info; ProfilerInfo profilerInfo = mAppProfiler.setupProfilerInfoLocked(thread, app, instr); @@ -5542,7 +5538,6 @@ public class ActivityManagerService extends IActivityManager.Stub public int sendIntentSender(IApplicationThread caller, IIntentSender target, IBinder allowlistToken, int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { - addCreatorToken(intent); if (target instanceof PendingIntentRecord) { final PendingIntentRecord originalRecord = (PendingIntentRecord) target; @@ -5584,19 +5579,23 @@ public class ActivityManagerService extends IActivityManager.Stub intent = new Intent(Intent.ACTION_MAIN); } try { + final int callingUid = Binder.getCallingUid(); + final String packageName; + final long token = Binder.clearCallingIdentity(); + try { + packageName = AppGlobals.getPackageManager().getNameForUid(callingUid); + } finally { + Binder.restoreCallingIdentity(token); + } + if (allowlistToken != null) { - final int callingUid = Binder.getCallingUid(); - final String packageName; - final long token = Binder.clearCallingIdentity(); - try { - packageName = AppGlobals.getPackageManager().getNameForUid(callingUid); - } finally { - Binder.restoreCallingIdentity(token); - } Slog.wtf(TAG, "Send a non-null allowlistToken to a non-PI target." + " Calling package: " + packageName + "; intent: " + intent + "; options: " + options); } + + addCreatorToken(intent, packageName); + target.send(code, intent, resolvedType, null, null, requiredPermission, options); } catch (RemoteException e) { @@ -12371,7 +12370,7 @@ public class ActivityManagerService extends IActivityManager.Stub continue; } endTime = SystemClock.currentThreadTimeMillis(); - hasSwapPss = mi.hasSwappedOutPss; + hasSwapPss = hasSwapPss || mi.hasSwappedOutPss; memtrackGraphics = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GRAPHICS); memtrackGl = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GL); } else { @@ -13049,7 +13048,7 @@ public class ActivityManagerService extends IActivityManager.Stub continue; } endTime = SystemClock.currentThreadTimeMillis(); - hasSwapPss = mi.hasSwappedOutPss; + hasSwapPss = hasSwapPss || mi.hasSwappedOutPss; } else { reportType = ProcessStats.ADD_PSS_EXTERNAL; startTime = SystemClock.currentThreadTimeMillis(); @@ -13628,7 +13627,7 @@ public class ActivityManagerService extends IActivityManager.Stub throws TransactionTooLargeException { enforceNotIsolatedCaller("startService"); enforceAllowedToStartOrBindServiceIfSdkSandbox(service); - addCreatorToken(service); + addCreatorToken(service, callingPackage); if (service != null) { // Refuse possible leaked file descriptors if (service.hasFileDescriptors()) { @@ -13890,7 +13889,7 @@ public class ActivityManagerService extends IActivityManager.Stub validateServiceInstanceName(instanceName); - addCreatorToken(service); + addCreatorToken(service, callingPackage); try { if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { final ComponentName cn = service.getComponent(); @@ -17174,7 +17173,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.v(TAG_SERVICE, "startServiceInPackage: " + service + " type=" + resolvedType); } - addCreatorToken(service); + addCreatorToken(service, callingPackage); final long origId = Binder.clearCallingIdentity(); ComponentName res; try { @@ -18002,8 +18001,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void addCreatorToken(Intent intent) { - ActivityManagerService.this.addCreatorToken(intent); + public void addCreatorToken(Intent intent, String creatorPackage) { + ActivityManagerService.this.addCreatorToken(intent, creatorPackage); } } @@ -19160,9 +19159,9 @@ public class ActivityManagerService extends IActivityManager.Stub private final Key mKeyFields; private final WeakReference<IntentCreatorToken> mRef; - public IntentCreatorToken(int creatorUid, Intent intent) { + public IntentCreatorToken(int creatorUid, String creatorPackage, Intent intent) { super(); - this.mKeyFields = new Key(creatorUid, intent); + this.mKeyFields = new Key(creatorUid, creatorPackage, intent); mRef = new WeakReference<>(this); } @@ -19170,7 +19169,10 @@ public class ActivityManagerService extends IActivityManager.Stub return mKeyFields.mCreatorUid; } - /** {@hide} */ + public String getCreatorPackage() { + return mKeyFields.mCreatorPackage; + } + public static boolean isValid(@NonNull Intent intent) { IBinder binder = intent.getCreatorToken(); IntentCreatorToken token = null; @@ -19178,7 +19180,8 @@ public class ActivityManagerService extends IActivityManager.Stub token = (IntentCreatorToken) binder; } return token != null && token.mKeyFields.equals( - new Key(token.mKeyFields.mCreatorUid, intent)); + new Key(token.mKeyFields.mCreatorUid, token.mKeyFields.mCreatorPackage, + intent)); } @Override @@ -19202,8 +19205,9 @@ public class ActivityManagerService extends IActivityManager.Stub } private static class Key { - private Key(int creatorUid, Intent intent) { + private Key(int creatorUid, String creatorPackage, Intent intent) { this.mCreatorUid = creatorUid; + this.mCreatorPackage = creatorPackage; this.mAction = intent.getAction(); this.mData = intent.getData(); this.mType = intent.getType(); @@ -19220,6 +19224,7 @@ public class ActivityManagerService extends IActivityManager.Stub } private final int mCreatorUid; + private final String mCreatorPackage; private final String mAction; private final Uri mData; private final String mType; @@ -19233,17 +19238,20 @@ public class ActivityManagerService extends IActivityManager.Stub if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key key = (Key) o; - return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags && Objects.equals( - mAction, key.mAction) && Objects.equals(mData, key.mData) - && Objects.equals(mType, key.mType) && Objects.equals(mPackage, - key.mPackage) && Objects.equals(mComponent, key.mComponent) + return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags + && Objects.equals(mCreatorPackage, key.mCreatorPackage) + && Objects.equals(mAction, key.mAction) + && Objects.equals(mData, key.mData) + && Objects.equals(mType, key.mType) + && Objects.equals(mPackage, key.mPackage) + && Objects.equals(mComponent, key.mComponent) && Objects.equals(mClipDataUris, key.mClipDataUris); } @Override public int hashCode() { - return Objects.hash(mCreatorUid, mAction, mData, mType, mPackage, mComponent, - mFlags, mClipDataUris); + return Objects.hash(mCreatorUid, mCreatorPackage, mAction, mData, mType, mPackage, + mComponent, mFlags, mClipDataUris); } } } @@ -19254,7 +19262,7 @@ public class ActivityManagerService extends IActivityManager.Stub * @param intent The given intent * @hide */ - public void addCreatorToken(@Nullable Intent intent) { + public void addCreatorToken(@Nullable Intent intent, String creatorPackage) { if (!preventIntentRedirect()) return; if (intent == null || intent.getExtraIntentKeys() == null) return; @@ -19267,7 +19275,7 @@ public class ActivityManagerService extends IActivityManager.Stub continue; } Slog.wtf(TAG, "A creator token is added to an intent."); - IBinder creatorToken = createIntentCreatorToken(extraIntent); + IBinder creatorToken = createIntentCreatorToken(extraIntent, creatorPackage); if (creatorToken != null) { extraIntent.setCreatorToken(creatorToken); } @@ -19280,15 +19288,15 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private IBinder createIntentCreatorToken(Intent intent) { + private IBinder createIntentCreatorToken(Intent intent, String creatorPackage) { if (IntentCreatorToken.isValid(intent)) return null; int creatorUid = getCallingUid(); - IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, intent); + IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent); IntentCreatorToken token; synchronized (sIntentCreatorTokenCache) { WeakReference<IntentCreatorToken> ref = sIntentCreatorTokenCache.get(key); if (ref == null || ref.get() == null) { - token = new IntentCreatorToken(creatorUid, intent); + token = new IntentCreatorToken(creatorUid, creatorPackage, intent); sIntentCreatorTokenCache.put(key, token.mRef); } else { token = ref.get(); diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 210301ec4c5e..02e2c391bb27 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -110,6 +110,7 @@ import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; +import android.os.ZygoteProcess; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -442,6 +443,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runSetForegroundServiceDelegate(pw); case "capabilities": return runCapabilities(pw); + case "set-app-zygote-preload-timeout": + return runSetAppZygotePreloadTimeout(pw); default: return handleDefaultCommands(cmd); } @@ -451,6 +454,15 @@ final class ActivityManagerShellCommand extends ShellCommand { return -1; } + int runSetAppZygotePreloadTimeout(PrintWriter pw) throws RemoteException { + final String timeout = getNextArgRequired(); + final int timeoutMs = Integer.parseInt(timeout); + + ZygoteProcess.setAppZygotePreloadTimeout(timeoutMs); + + return 0; + } + int runCapabilities(PrintWriter pw) throws RemoteException { final PrintWriter err = getErrPrintWriter(); boolean outputAsProtobuf = false; @@ -4623,6 +4635,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" capabilities [--protobuf]"); pw.println(" Output am supported features (text format). Options are:"); pw.println(" --protobuf: format output using protobuffer"); + pw.println(" set-app-zygote-preload-timeout <TIMEOUT_IN_MS>"); + pw.println(" Set the timeout for preloading code in the app-zygote"); Intent.printIntentArgsHelp(pw, ""); } } diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java index 15f1085b7125..a00cac6aba4f 100644 --- a/services/core/java/com/android/server/am/BroadcastController.java +++ b/services/core/java/com/android/server/am/BroadcastController.java @@ -258,6 +258,7 @@ class BroadcastController { final StringBuilder sb = new StringBuilder("registerReceiver: "); sb.append(Binder.getCallingUid()); sb.append('/'); sb.append(receiverId == null ? "null" : receiverId); sb.append('/'); + sb.append("p:"); sb.append(filter.getPriority()); sb.append('/'); final int actionsCount = filter.safeCountActions(); if (actionsCount > 0) { for (int i = 0; i < actionsCount; ++i) { diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java index ae5ae0133e1b..4f0ea51bac2a 100644 --- a/services/core/java/com/android/server/am/ContentProviderConnection.java +++ b/services/core/java/com/android/server/am/ContentProviderConnection.java @@ -40,7 +40,7 @@ public final class ContentProviderConnection extends Binder implements public final String clientPackage; public AssociationState.SourceState association; public final long createTime; - private Object mProcStatsLock; // Internal lock for accessing AssociationState + private volatile Object mProcStatsLock; // Internal lock for accessing AssociationState /** * Internal lock that guards access to the two counters. @@ -118,19 +118,25 @@ public final class ContentProviderConnection extends Binder implements * Track the given proc state change. */ public void trackProcState(int procState, int seq) { - if (association != null) { - synchronized (mProcStatsLock) { + if (association == null) { + return; // early exit to optimize on oomadj cycles + } + synchronized (mProcStatsLock) { + if (association != null) { // due to race-conditions, association may have become null association.trackProcState(procState, seq, SystemClock.uptimeMillis()); } } } public void stopAssociation() { - if (association != null) { - synchronized (mProcStatsLock) { + if (association == null) { + return; // early exit to optimize on oomadj cycles + } + synchronized (mProcStatsLock) { + if (association != null) { // due to race-conditions, association may have become null association.stop(); + association = null; } - association = null; } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 776a3455acc4..f60ee66cb236 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -3283,7 +3283,12 @@ public class OomAdjuster { baseCapabilities = PROCESS_CAPABILITY_ALL; // BFSL allowed break; case PROCESS_STATE_BOUND_TOP: - baseCapabilities = PROCESS_CAPABILITY_BFSL; + if (app.getActiveInstrumentation() != null) { + baseCapabilities = PROCESS_CAPABILITY_BFSL | + PROCESS_CAPABILITY_ALL_IMPLICIT; + } else { + baseCapabilities = PROCESS_CAPABILITY_BFSL; + } break; case PROCESS_STATE_FOREGROUND_SERVICE: if (app.getActiveInstrumentation() != null) { diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 6857b6bcde15..3fb06a75d79f 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -432,6 +432,14 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } } + /** + * get package name of the PendingIntent sender. + * @return package name of the PendingIntent sender. + */ + public String getPackageName() { + return key.packageName; + } + @Deprecated public int sendInner(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 5236b0399f25..cdb01889c139 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -813,10 +813,8 @@ public final class ProcessList { private final Object mProcessChangeLock = new Object(); /** - * All of the applications we currently have running organized by name. - * The keys are strings of the application package name (as - * returned by the package manager), and the keys are ApplicationRecord - * objects. + * All of the processes that are running organized by name. + * The keys are process names and the values are the associated ProcessRecord objects. */ @CompositeRWLock({"mService", "mProcLock"}) private final MyProcessMap mProcessNames = new MyProcessMap(); @@ -5189,6 +5187,7 @@ public final class ProcessList { if (ai != null) { if (ai.packageName.equals(app.info.packageName)) { app.info = ai; + app.getWindowProcessController().updateApplicationInfo(ai); PlatformCompatCache.getInstance() .onApplicationInfoChanged(ai); } diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index a815f72ca09e..2f4e8bbea1d7 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -150,6 +150,7 @@ public class SettingsToPropertiesMapper { "art_mainline", "art_performance", "attack_tools", + "automotive_cast", "avic", "desktop_firmware", "biometrics", diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index adf0e640f6bf..d67fea09f945 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -225,3 +225,11 @@ flag { description: "Migrate OomAdjuster pulled device state to a push model" bug: "302575389" } + +flag { + name: "oomadjuster_cached_app_tiers" + namespace: "system_performance" + is_fixed_read_only: true + description: "Assign cached oom_score_adj in tiers." + bug: "369893532" +} diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 09386ae2899d..8a9858548806 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -115,7 +115,7 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions> } @LockoutTracker.LockoutMode - public int handleFailedAttempt(int userId) { + private int handleFailedAttempt(int userId) { if (mLockoutTracker != null) { mLockoutTracker.addFailedAttemptForUser(getTargetUserId()); } diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java index 9a0ee034a8f2..a4804e1887fe 100644 --- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java +++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java @@ -50,8 +50,10 @@ public final class BrightnessReason { public static final int MODIFIER_THROTTLED = 0x8; public static final int MODIFIER_MIN_LUX = 0x10; public static final int MODIFIER_MIN_USER_SET_LOWER_BOUND = 0x20; + public static final int MODIFIER_STYLUS_UNDER_USE = 0x40; public static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR - | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND; + | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND + | MODIFIER_STYLUS_UNDER_USE; // ADJUSTMENT_* // These things can happen at any point, even if the main brightness reason doesn't @@ -158,6 +160,9 @@ public final class BrightnessReason { if ((mModifier & MODIFIER_MIN_USER_SET_LOWER_BOUND) != 0) { sb.append(" user_min_pref"); } + if ((mModifier & MODIFIER_STYLUS_UNDER_USE) != 0) { + sb.append(" stylus_under_use"); + } int strlen = sb.length(); if (sb.charAt(strlen - 1) == '[') { sb.setLength(strlen - 2); diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java index 71fdaf3f85b6..4bd980822e46 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java @@ -110,6 +110,9 @@ public final class DisplayBrightnessController { @VisibleForTesting AutomaticBrightnessController mAutomaticBrightnessController; + // True if the stylus is being used + private boolean mIsStylusBeingUsed; + /** * The constructor of DisplayBrightnessController. */ @@ -460,6 +463,8 @@ public final class DisplayBrightnessController { writer.println(" mScreenBrightnessDefault=" + mScreenBrightnessDefault); writer.println(" mPersistBrightnessNitsForDefaultDisplay=" + mPersistBrightnessNitsForDefaultDisplay); + writer.println(" mIsStylusBeingUsed=" + + mIsStylusBeingUsed); synchronized (mLock) { writer.println(" mPendingScreenBrightness=" + mPendingScreenBrightness); writer.println(" mCurrentScreenBrightness=" + mCurrentScreenBrightness); @@ -505,7 +510,12 @@ public final class DisplayBrightnessController { * Notifies if the stylus is currently being used or not. */ public void setStylusBeingUsed(boolean isEnabled) { - // Todo(b/369977976) - Disable the auto-brightness strategy + mIsStylusBeingUsed = isEnabled; + } + + @VisibleForTesting + boolean isStylusBeingUsed() { + return mIsStylusBeingUsed; } @VisibleForTesting @@ -626,13 +636,14 @@ public final class DisplayBrightnessController { lastUserSetScreenBrightness = mLastUserSetScreenBrightness; } return new StrategySelectionRequest(displayPowerRequest, targetDisplayState, - lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession); + lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession, + mIsStylusBeingUsed); } private StrategyExecutionRequest constructStrategyExecutionRequest( DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) { float currentScreenBrightness = getCurrentBrightness(); return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness, - mUserSetScreenBrightnessUpdated); + mUserSetScreenBrightnessUpdated, mIsStylusBeingUsed); } } diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java index 06890e72f068..ded7447c5fbc 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java @@ -306,7 +306,8 @@ public class DisplayBrightnessStrategySelector { strategySelectionRequest.getDisplayPowerRequest().useNormalBrightnessForDoze, strategySelectionRequest.getLastUserSetScreenBrightness(), strategySelectionRequest.isUserSetBrightnessChanged()); - return mAutomaticBrightnessStrategy1.isAutoBrightnessValid(); + return !strategySelectionRequest.isStylusBeingUsed() + && mAutomaticBrightnessStrategy1.isAutoBrightnessValid(); } private StrategySelectionNotifyRequest constructStrategySelectionNotifyRequest( diff --git a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java index 304640b884ef..7a07c4fc22bf 100644 --- a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java +++ b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java @@ -32,11 +32,15 @@ public final class StrategyExecutionRequest { // Represents if the user set screen brightness was changed or not. private boolean mUserSetBrightnessChanged; + private boolean mIsStylusBeingUsed; + public StrategyExecutionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, - float currentScreenBrightness, boolean userSetBrightnessChanged) { + float currentScreenBrightness, boolean userSetBrightnessChanged, + boolean isStylusBeingUsed) { mDisplayPowerRequest = displayPowerRequest; mCurrentScreenBrightness = currentScreenBrightness; mUserSetBrightnessChanged = userSetBrightnessChanged; + mIsStylusBeingUsed = isStylusBeingUsed; } public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() { @@ -51,6 +55,10 @@ public final class StrategyExecutionRequest { return mUserSetBrightnessChanged; } + public boolean isStylusBeingUsed() { + return mIsStylusBeingUsed; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof StrategyExecutionRequest)) { @@ -59,12 +67,13 @@ public final class StrategyExecutionRequest { StrategyExecutionRequest other = (StrategyExecutionRequest) obj; return Objects.equals(mDisplayPowerRequest, other.getDisplayPowerRequest()) && mCurrentScreenBrightness == other.getCurrentScreenBrightness() - && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged(); + && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged() + && mIsStylusBeingUsed == other.isStylusBeingUsed(); } @Override public int hashCode() { return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness, - mUserSetBrightnessChanged); + mUserSetBrightnessChanged, mIsStylusBeingUsed); } } diff --git a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java index aa2f23ef9ec1..5c1f03d877a6 100644 --- a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java +++ b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java @@ -40,15 +40,19 @@ public final class StrategySelectionRequest { private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; + private boolean mIsStylusBeingUsed; + public StrategySelectionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int targetDisplayState, float lastUserSetScreenBrightness, boolean userSetBrightnessChanged, - DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) { + DisplayManagerInternal.DisplayOffloadSession displayOffloadSession, + boolean isStylusBeingUsed) { mDisplayPowerRequest = displayPowerRequest; mTargetDisplayState = targetDisplayState; mLastUserSetScreenBrightness = lastUserSetScreenBrightness; mUserSetBrightnessChanged = userSetBrightnessChanged; mDisplayOffloadSession = displayOffloadSession; + mIsStylusBeingUsed = isStylusBeingUsed; } public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() { @@ -72,6 +76,10 @@ public final class StrategySelectionRequest { return mDisplayOffloadSession; } + public boolean isStylusBeingUsed() { + return mIsStylusBeingUsed; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof StrategySelectionRequest)) { @@ -82,12 +90,14 @@ public final class StrategySelectionRequest { && mTargetDisplayState == other.getTargetDisplayState() && mLastUserSetScreenBrightness == other.getLastUserSetScreenBrightness() && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged() - && mDisplayOffloadSession.equals(other.getDisplayOffloadSession()); + && mDisplayOffloadSession.equals(other.getDisplayOffloadSession()) + && mIsStylusBeingUsed == other.isStylusBeingUsed(); } @Override public int hashCode() { return Objects.hash(mDisplayPowerRequest, mTargetDisplayState, - mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession); + mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession, + mIsStylusBeingUsed); } } diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java index 7c0c9312888e..b9de34a80bda 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java @@ -37,6 +37,9 @@ public class FallbackBrightnessStrategy implements DisplayBrightnessStrategy{ StrategyExecutionRequest strategyExecutionRequest) { BrightnessReason brightnessReason = new BrightnessReason(); brightnessReason.setReason(BrightnessReason.REASON_MANUAL); + if (strategyExecutionRequest.isStylusBeingUsed()) { + brightnessReason.setModifier(BrightnessReason.MODIFIER_STYLUS_UNDER_USE); + } return new DisplayBrightnessState.Builder() .setBrightness(strategyExecutionRequest.getCurrentScreenBrightness()) .setBrightnessReason(brightnessReason) diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 939aad469bd8..83044c202a04 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1536,7 +1536,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); } final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); - return settings.getMethodMap().get(settings.getSelectedInputMethod()); + final String selectedImeId; + if (Flags.consistentGetCurrentInputMethodInfo()) { + final var bindingController = getInputMethodBindingController(userId); + synchronized (ImfLock.class) { + selectedImeId = bindingController.getSelectedMethodId(); + } + } else { + selectedImeId = settings.getSelectedInputMethod(); + } + return settings.getMethodMap().get(selectedImeId); } @BinderThread diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 636854b85ee4..d1576c5cca4f 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -17,118 +17,63 @@ package com.android.server.integrity; import static android.content.Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION; -import static android.content.Intent.EXTRA_LONG_VERSION_CODE; -import static android.content.Intent.EXTRA_ORIGINATING_UID; -import static android.content.Intent.EXTRA_PACKAGE_NAME; import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS; import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE; import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS; -import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED; import static android.content.integrity.IntegrityUtils.getHexDigest; import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID; import android.annotation.BinderThread; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; -import android.content.integrity.AppInstallMetadata; import android.content.integrity.IAppIntegrityManager; import android.content.integrity.Rule; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; -import android.content.pm.Signature; -import android.content.pm.SigningDetails; -import android.content.pm.parsing.result.ParseResult; -import android.content.pm.parsing.result.ParseTypeImpl; import android.net.Uri; import android.os.Binder; -import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.provider.Settings; import android.util.Pair; import android.util.Slog; -import android.util.apk.SourceStampVerificationResult; -import android.util.apk.SourceStampVerifier; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.parsing.PackageParser2; -import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; -import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; -import com.android.server.integrity.model.IntegrityCheckResult; import com.android.server.integrity.model.RuleMetadata; -import com.android.server.pm.PackageManagerServiceUtils; -import com.android.server.pm.parsing.PackageParserUtils; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.IOException; -import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.Stream; /** Implementation of {@link AppIntegrityManagerService}. */ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { - /** - * This string will be used as the "installer" for formula evaluation when the app's installer - * cannot be determined. - * - * <p>This may happen for various reasons. e.g., the installing app's package name may not match - * its UID. - */ - private static final String UNKNOWN_INSTALLER = ""; - /** - * This string will be used as the "installer" for formula evaluation when the app is being - * installed via ADB. - */ - public static final String ADB_INSTALLER = "adb"; private static final String TAG = "AppIntegrityManagerServiceImpl"; private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; - private static final String BASE_APK_FILE = "base.apk"; - private static final String ALLOWED_INSTALLERS_METADATA_NAME = "allowed-installers"; - private static final String ALLOWED_INSTALLER_DELIMITER = ","; - private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|"; public static final boolean DEBUG_INTEGRITY_COMPONENT = false; - private static final Set<String> PACKAGE_INSTALLER = - new HashSet<>( - Arrays.asList( - "com.google.android.packageinstaller", "com.android.packageinstaller")); - // Access to files inside mRulesDir is protected by mRulesLock; private final Context mContext; private final Handler mHandler; private final PackageManagerInternal mPackageManagerInternal; - private final Supplier<PackageParser2> mParserSupplier; private final IntegrityFileManager mIntegrityFileManager; /** Create an instance of {@link AppIntegrityManagerServiceImpl}. */ @@ -139,7 +84,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { return new AppIntegrityManagerServiceImpl( context, LocalServices.getService(PackageManagerInternal.class), - PackageParserUtils::forParsingFileWithDefaults, IntegrityFileManager.getInstance(), handlerThread.getThreadHandler()); } @@ -148,12 +92,10 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { AppIntegrityManagerServiceImpl( Context context, PackageManagerInternal packageManagerInternal, - Supplier<PackageParser2> parserSupplier, IntegrityFileManager integrityFileManager, Handler handler) { mContext = context; mPackageManagerInternal = packageManagerInternal; - mParserSupplier = parserSupplier; mIntegrityFileManager = integrityFileManager; mHandler = handler; @@ -263,148 +205,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private void handleIntegrityVerification(Intent intent) { int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1); - - try { - if (DEBUG_INTEGRITY_COMPONENT) { - Slog.d(TAG, "Received integrity verification intent " + intent.toString()); - Slog.d(TAG, "Extras " + intent.getExtras()); - } - - String installerPackageName = getInstallerPackageName(intent); - - // Skip integrity verification if the verifier is doing the install. - if (!integrityCheckIncludesRuleProvider() && isRuleProvider(installerPackageName)) { - if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i(TAG, "Verifier doing the install. Skipping integrity check."); - } - mPackageManagerInternal.setIntegrityVerificationResult( - verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); - return; - } - - String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); - - Pair<SigningDetails, Bundle> packageSigningAndMetadata = - getPackageSigningAndMetadata(intent.getData()); - if (packageSigningAndMetadata == null) { - Slog.w(TAG, "Cannot parse package " + packageName); - // We can't parse the package. - mPackageManagerInternal.setIntegrityVerificationResult( - verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); - return; - } - - var signingDetails = packageSigningAndMetadata.first; - List<String> appCertificates = getCertificateFingerprint(packageName, signingDetails); - List<String> appCertificateLineage = getCertificateLineage(packageName, signingDetails); - List<String> installerCertificates = - getInstallerCertificateFingerprint(installerPackageName); - - AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder(); - - builder.setPackageName(getPackageNameNormalized(packageName)); - builder.setAppCertificates(appCertificates); - builder.setAppCertificateLineage(appCertificateLineage); - builder.setVersionCode(intent.getLongExtra(EXTRA_LONG_VERSION_CODE, -1)); - builder.setInstallerName(getPackageNameNormalized(installerPackageName)); - builder.setInstallerCertificates(installerCertificates); - builder.setIsPreInstalled(isSystemApp(packageName)); - - Map<String, String> allowedInstallers = - getAllowedInstallers(packageSigningAndMetadata.second); - builder.setAllowedInstallersAndCert(allowedInstallers); - extractSourceStamp(intent.getData(), builder); - - AppInstallMetadata appInstallMetadata = builder.build(); - - if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i( - TAG, - "To be verified: " - + appInstallMetadata - + " installers " - + allowedInstallers); - } - IntegrityCheckResult result = IntegrityCheckResult.allow(); - if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) { - Slog.i( - TAG, - String.format( - "Integrity check of %s result: %s due to %s", - packageName, result.getEffect(), result.getMatchedRules())); - } - - mPackageManagerInternal.setIntegrityVerificationResult( - verificationId, - result.getEffect() == IntegrityCheckResult.Effect.ALLOW - ? PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW - : PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT); - } catch (IllegalArgumentException e) { - // This exception indicates something is wrong with the input passed by package manager. - // e.g., someone trying to trick the system. We block installs in this case. - Slog.e(TAG, "Invalid input to integrity verification", e); - mPackageManagerInternal.setIntegrityVerificationResult( - verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT); - } catch (Exception e) { - // Other exceptions indicate an error within the integrity component implementation and - // we allow them. - Slog.e(TAG, "Error handling integrity verification", e); - mPackageManagerInternal.setIntegrityVerificationResult( - verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); - } - } - - /** - * Verify the UID and return the installer package name. - * - * @return the package name of the installer, or null if it cannot be determined or it is - * installed via adb. - */ - @Nullable - private String getInstallerPackageName(Intent intent) { - String installer = - intent.getStringExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE); - if (PackageManagerServiceUtils.isInstalledByAdb(installer)) { - return ADB_INSTALLER; - } - int installerUid = intent.getIntExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID, -1); - if (installerUid < 0) { - Slog.e( - TAG, - "Installer cannot be determined: installer: " - + installer - + " installer UID: " - + installerUid); - return UNKNOWN_INSTALLER; - } - - // Verify that the installer UID actually contains the package. Note that comparing UIDs - // is not safe since context's uid can change in different settings; e.g. Android Auto. - if (!getPackageListForUid(installerUid).contains(installer)) { - return UNKNOWN_INSTALLER; - } - - // At this time we can trust "installer". - - // A common way for apps to install packages is to send an intent to PackageInstaller. In - // that case, the installer will always show up as PackageInstaller which is not what we - // want. - if (PACKAGE_INSTALLER.contains(installer)) { - int originatingUid = intent.getIntExtra(EXTRA_ORIGINATING_UID, -1); - if (originatingUid < 0) { - Slog.e(TAG, "Installer is package installer but originating UID not found."); - return UNKNOWN_INSTALLER; - } - List<String> installerPackages = getPackageListForUid(originatingUid); - if (installerPackages.isEmpty()) { - Slog.e(TAG, "No package found associated with originating UID " + originatingUid); - return UNKNOWN_INSTALLER; - } - // In the case of multiple package sharing a UID, we just return the first one. - return installerPackages.get(0); - } - - return installer; + mPackageManagerInternal.setIntegrityVerificationResult( + verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); } /** We will use the SHA256 digest of a package name if it is more than 32 bytes long. */ @@ -422,264 +224,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } } - private List<String> getInstallerCertificateFingerprint(String installer) { - if (installer.equals(ADB_INSTALLER) || installer.equals(UNKNOWN_INSTALLER)) { - return Collections.emptyList(); - } - var installerPkg = mPackageManagerInternal.getPackage(installer); - if (installerPkg == null) { - Slog.w(TAG, "Installer package " + installer + " not found."); - return Collections.emptyList(); - } - return getCertificateFingerprint(installerPkg.getPackageName(), - installerPkg.getSigningDetails()); - } - - private List<String> getCertificateFingerprint(@NonNull String packageName, - @NonNull SigningDetails signingDetails) { - ArrayList<String> certificateFingerprints = new ArrayList(); - for (Signature signature : getSignatures(packageName, signingDetails)) { - certificateFingerprints.add(getFingerprint(signature)); - } - return certificateFingerprints; - } - - private List<String> getCertificateLineage(@NonNull String packageName, - @NonNull SigningDetails signingDetails) { - ArrayList<String> certificateLineage = new ArrayList(); - for (Signature signature : getSignatureLineage(packageName, signingDetails)) { - certificateLineage.add(getFingerprint(signature)); - } - return certificateLineage; - } - - /** Get the allowed installers and their associated certificate hashes from <meta-data> tag. */ - private Map<String, String> getAllowedInstallers(@Nullable Bundle metaData) { - Map<String, String> packageCertMap = new HashMap<>(); - if (metaData != null) { - String allowedInstallers = metaData.getString(ALLOWED_INSTALLERS_METADATA_NAME); - if (allowedInstallers != null) { - // parse the metadata for certs. - String[] installerCertPairs = allowedInstallers.split(ALLOWED_INSTALLER_DELIMITER); - for (String packageCertPair : installerCertPairs) { - String[] packageAndCert = - packageCertPair.split(INSTALLER_PACKAGE_CERT_DELIMITER); - if (packageAndCert.length == 2) { - String packageName = getPackageNameNormalized(packageAndCert[0]); - String cert = packageAndCert[1]; - packageCertMap.put(packageName, cert); - } else if (packageAndCert.length == 1) { - packageCertMap.put( - getPackageNameNormalized(packageAndCert[0]), - INSTALLER_CERTIFICATE_NOT_EVALUATED); - } - } - } - } - - return packageCertMap; - } - - /** Extract the source stamp embedded in the APK, if present. */ - private void extractSourceStamp(Uri dataUri, AppInstallMetadata.Builder appInstallMetadata) { - File installationPath = getInstallationPath(dataUri); - if (installationPath == null) { - throw new IllegalArgumentException("Installation path is null, package not found"); - } - - SourceStampVerificationResult sourceStampVerificationResult; - if (installationPath.isDirectory()) { - try (Stream<Path> filesList = Files.list(installationPath.toPath())) { - List<String> apkFiles = - filesList - .map(path -> path.toAbsolutePath().toString()) - .filter(str -> str.endsWith(".apk")) - .collect(Collectors.toList()); - sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles); - } catch (IOException e) { - throw new IllegalArgumentException("Could not read APK directory"); - } - } else { - sourceStampVerificationResult = - SourceStampVerifier.verify(installationPath.getAbsolutePath()); - } - - appInstallMetadata.setIsStampPresent(sourceStampVerificationResult.isPresent()); - appInstallMetadata.setIsStampVerified(sourceStampVerificationResult.isVerified()); - // A verified stamp is set to be trusted. - appInstallMetadata.setIsStampTrusted(sourceStampVerificationResult.isVerified()); - if (sourceStampVerificationResult.isVerified()) { - X509Certificate sourceStampCertificate = - (X509Certificate) sourceStampVerificationResult.getCertificate(); - // Sets source stamp certificate digest. - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - byte[] certificateDigest = digest.digest(sourceStampCertificate.getEncoded()); - appInstallMetadata.setStampCertificateHash(getHexDigest(certificateDigest)); - } catch (NoSuchAlgorithmException | CertificateEncodingException e) { - throw new IllegalArgumentException( - "Error computing source stamp certificate digest", e); - } - } - } - - private static Signature[] getSignatures(@NonNull String packageName, - @NonNull SigningDetails signingDetails) { - Signature[] signatures = signingDetails.getSignatures(); - if (signatures == null || signatures.length < 1) { - throw new IllegalArgumentException("Package signature not found in " + packageName); - } - - // We are only interested in evaluating the active signatures. - return signatures; - } - - private static Signature[] getSignatureLineage(@NonNull String packageName, - @NonNull SigningDetails signingDetails) { - // Obtain the active signatures of the package. - Signature[] signatureLineage = getSignatures(packageName, signingDetails); - - var pastSignatures = signingDetails.getPastSigningCertificates(); - // Obtain the past signatures of the package. - if (signatureLineage.length == 1 && !ArrayUtils.isEmpty(pastSignatures)) { - // Merge the signatures and return. - Signature[] allSignatures = - new Signature[signatureLineage.length + pastSignatures.length]; - int i; - for (i = 0; i < signatureLineage.length; i++) { - allSignatures[i] = signatureLineage[i]; - } - for (int j = 0; j < pastSignatures.length; j++) { - allSignatures[i] = pastSignatures[j]; - i++; - } - signatureLineage = allSignatures; - } - - return signatureLineage; - } - - private static String getFingerprint(Signature cert) { - InputStream input = new ByteArrayInputStream(cert.toByteArray()); - - CertificateFactory factory; - try { - factory = CertificateFactory.getInstance("X509"); - } catch (CertificateException e) { - throw new RuntimeException("Error getting CertificateFactory", e); - } - X509Certificate certificate = null; - try { - if (factory != null) { - certificate = (X509Certificate) factory.generateCertificate(input); - } - } catch (CertificateException e) { - throw new RuntimeException("Error getting X509Certificate", e); - } - - if (certificate == null) { - throw new RuntimeException("X509 Certificate not found"); - } - - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - byte[] publicKey = digest.digest(certificate.getEncoded()); - return getHexDigest(publicKey); - } catch (NoSuchAlgorithmException | CertificateEncodingException e) { - throw new IllegalArgumentException("Error error computing fingerprint", e); - } - } - - @Nullable - private Pair<SigningDetails, Bundle> getPackageSigningAndMetadata(Uri dataUri) { - File installationPath = getInstallationPath(dataUri); - if (installationPath == null) { - throw new IllegalArgumentException("Installation path is null, package not found"); - } - - try (PackageParser2 parser = mParserSupplier.get()) { - var pkg = parser.parsePackage(installationPath, 0, false); - // APK signatures is already verified elsewhere in PackageManager. We do not need to - // verify it again since it could cause a timeout for large APKs. - final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); - final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails( - input, pkg, /* skipVerify= */ true); - if (result.isError()) { - Slog.w(TAG, result.getErrorMessage(), result.getException()); - return null; - } - return Pair.create(result.getResult(), pkg.getMetaData()); - } catch (Exception e) { - Slog.w(TAG, "Exception reading " + dataUri, e); - return null; - } - } - - private PackageInfo getMultiApkInfo(File multiApkDirectory) { - // The base apk will normally be called base.apk - File baseFile = new File(multiApkDirectory, BASE_APK_FILE); - PackageInfo basePackageInfo = - mContext.getPackageManager() - .getPackageArchiveInfo( - baseFile.getAbsolutePath(), - PackageManager.GET_SIGNING_CERTIFICATES - | PackageManager.GET_META_DATA); - - if (basePackageInfo == null) { - for (File apkFile : multiApkDirectory.listFiles()) { - if (apkFile.isDirectory()) { - continue; - } - - // If we didn't find a base.apk, then try to parse each apk until we find the one - // that succeeds. - try { - basePackageInfo = - mContext.getPackageManager() - .getPackageArchiveInfo( - apkFile.getAbsolutePath(), - PackageManager.GET_SIGNING_CERTIFICATES - | PackageManager.GET_META_DATA); - } catch (Exception e) { - // Some of the splits may not contain a valid android manifest. It is an - // expected exception. We still log it nonetheless but we should keep looking. - Slog.w(TAG, "Exception reading " + apkFile, e); - } - if (basePackageInfo != null) { - Slog.i(TAG, "Found package info from " + apkFile); - break; - } - } - } - - if (basePackageInfo == null) { - throw new IllegalArgumentException( - "Base package info cannot be found from installation directory"); - } - - return basePackageInfo; - } - - private File getInstallationPath(Uri dataUri) { - if (dataUri == null) { - throw new IllegalArgumentException("Null data uri"); - } - - String scheme = dataUri.getScheme(); - if (!"file".equalsIgnoreCase(scheme)) { - throw new IllegalArgumentException("Unsupported scheme for " + dataUri); - } - - File installationPath = new File(dataUri.getPath()); - if (!installationPath.exists()) { - throw new IllegalArgumentException("Cannot find file for " + dataUri); - } - if (!installationPath.canRead()) { - throw new IllegalArgumentException("Cannot read file for " + dataUri); - } - return installationPath; - } - private String getCallerPackageNameOrThrow(int callingUid) { String callerPackageName = getCallingRulePusherPackageName(callingUid); if (callerPackageName == null) { @@ -715,15 +259,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { return allowedCallingPackages.isEmpty() ? null : allowedCallingPackages.get(0); } - private boolean isRuleProvider(String installerPackageName) { - for (String ruleProvider : getAllowedRuleProviderSystemApps()) { - if (ruleProvider.matches(installerPackageName)) { - return true; - } - } - return false; - } - private List<String> getAllowedRuleProviderSystemApps() { List<String> integrityRuleProviders = Arrays.asList( @@ -751,14 +286,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } } - private boolean integrityCheckIncludesRuleProvider() { - return Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, - 0) - == 1; - } - private List<String> getPackageListForUid(int uid) { try { return Arrays.asList(mContext.getPackageManager().getPackagesForUid(uid)); diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java index c79f41d32b29..25e1c94aa689 100644 --- a/services/core/java/com/android/server/media/AudioManagerRouteController.java +++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java @@ -420,7 +420,7 @@ import java.util.Objects; // to derive a name ourselves from the type instead. String deviceName = audioDeviceInfo.getPort().name(); - if (!TextUtils.isEmpty(address)) { + if (mBluetoothRouteController.containsBondedDeviceWithAddress(address)) { routeId = mBluetoothRouteController.getRouteIdForBluetoothAddress(address); deviceName = mBluetoothRouteController.getNameForBluetoothAddress(address); } diff --git a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java index 8b65ea305ad8..c79d6e5400cd 100644 --- a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java +++ b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java @@ -141,6 +141,11 @@ import java.util.stream.Collectors; mContext.unregisterReceiver(mDeviceStateChangedReceiver); } + /** Returns true if the given address corresponds to a currently-bonded Bluetooth device. */ + public synchronized boolean containsBondedDeviceWithAddress(@Nullable String address) { + return mAddressToBondedDevice.containsKey(address); + } + @Nullable public synchronized String getRouteIdForBluetoothAddress(@Nullable String address) { BluetoothDevice bluetoothDevice = mAddressToBondedDevice.get(address); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index d1d5d4868c6f..27bc1cf3e631 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -677,24 +677,15 @@ class MediaRouter2ServiceImpl { @NonNull IMediaRouter2Manager manager, int requestId, @NonNull RoutingSessionInfo oldSession, - @NonNull MediaRoute2Info route, - @NonNull UserHandle transferInitiatorUserHandle, - @NonNull String transferInitiatorPackageName) { + @NonNull MediaRoute2Info route) { Objects.requireNonNull(manager, "manager must not be null"); Objects.requireNonNull(oldSession, "oldSession must not be null"); Objects.requireNonNull(route, "route must not be null"); - Objects.requireNonNull(transferInitiatorUserHandle); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - requestCreateSessionWithManagerLocked( - requestId, - manager, - oldSession, - route, - transferInitiatorUserHandle, - transferInitiatorPackageName); + requestCreateSessionWithManagerLocked(requestId, manager, oldSession, route); } } finally { Binder.restoreCallingIdentity(token); @@ -1738,9 +1729,7 @@ class MediaRouter2ServiceImpl { int requestId, @NonNull IMediaRouter2Manager manager, @NonNull RoutingSessionInfo oldSession, - @NonNull MediaRoute2Info route, - @NonNull UserHandle transferInitiatorUserHandle, - @NonNull String transferInitiatorPackageName) { + @NonNull MediaRoute2Info route) { ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder()); if (managerRecord == null) { return; diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 363b8e4228b0..68e195d7f079 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -607,16 +607,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub IMediaRouter2Manager manager, int requestId, RoutingSessionInfo oldSession, - MediaRoute2Info route, - UserHandle transferInitiatorUserHandle, - String transferInitiatorPackageName) { - mService2.requestCreateSessionWithManager( - manager, - requestId, - oldSession, - route, - transferInitiatorUserHandle, - transferInitiatorPackageName); + MediaRoute2Info route) { + mService2.requestCreateSessionWithManager(manager, requestId, oldSession, route); } // Binder call diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index e7e519ede768..e0913ccbc7f7 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -28,6 +28,7 @@ import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK; import static android.media.projection.ReviewGrantedConsentResult.UNKNOWN; +import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -73,6 +74,7 @@ import android.os.PermissionEnforcer; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; +import android.provider.Settings; import android.util.ArrayMap; import android.util.Slog; import android.view.ContentRecordingSession; @@ -195,6 +197,15 @@ public final class MediaProjectionManagerService extends SystemService if (mProjectionGrant == null || mProjectionGrant.packageName == null) { return false; } + boolean disableScreenShareProtections = Settings.Global.getInt( + getContext().getContentResolver(), + DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0) != 0; + if (disableScreenShareProtections) { + Slog.v(TAG, + "Allowing keyguard capture as screenshare protections are disabled."); + return true; + } + if (mPackageManager.checkPermission(RECORD_SENSITIVE_CONTENT, mProjectionGrant.packageName) == PackageManager.PERMISSION_GRANTED) { @@ -226,7 +237,8 @@ public final class MediaProjectionManagerService extends SystemService void onKeyguardLockedStateChanged(boolean isKeyguardLocked) { if (!isKeyguardLocked) return; synchronized (mLock) { - if (mProjectionGrant != null && !canCaptureKeyguard()) { + if (mProjectionGrant != null && !canCaptureKeyguard() + && mProjectionGrant.mVirtualDisplayId != INVALID_DISPLAY) { Slog.d(TAG, "Content Recording: Stopped MediaProjection" + " due to keyguard lock"); mProjectionGrant.stop(); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index a6f4c0e597d1..2a3be1e119bf 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -3100,11 +3100,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } synchronized (mUidRulesFirstLock) { - final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE); - policy |= oldPolicy; - if (oldPolicy != policy) { - setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); - mLogger.uidPolicyChanged(uid, oldPolicy, policy); + final long token = Binder.clearCallingIdentity(); + try { + final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE); + policy |= oldPolicy; + if (oldPolicy != policy) { + setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); + mLogger.uidPolicyChanged(uid, oldPolicy, policy); + } + } finally { + Binder.restoreCallingIdentity(token); } } } @@ -3119,11 +3124,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } synchronized (mUidRulesFirstLock) { - final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE); - policy = oldPolicy & ~policy; - if (oldPolicy != policy) { - setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); - mLogger.uidPolicyChanged(uid, oldPolicy, policy); + final long token = Binder.clearCallingIdentity(); + try { + final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE); + policy = oldPolicy & ~policy; + if (oldPolicy != policy) { + setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); + mLogger.uidPolicyChanged(uid, oldPolicy, policy); + } + } finally { + Binder.restoreCallingIdentity(token); } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index f3d6a2dd0a75..d5d4070ee4c3 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -44,7 +44,7 @@ public interface NotificationManagerInternal { void onConversationRemoved(String pkg, int uid, Set<String> shortcuts); - /** Get the number of notification channels for a given package */ + /** Get the number of app created notification channels for a given package */ int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted); /** Does the specified package/uid have permission to post notifications? */ diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 828e02ce3edc..62d762244617 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4392,8 +4392,9 @@ public class NotificationManagerService extends SystemService { List<NotificationChannel> channels = channelsList.getList(); final int channelsSize = channels.size(); ParceledListSlice<NotificationChannel> oldChannels = - mPreferencesHelper.getNotificationChannels(pkg, uid, true); - final boolean hadChannel = oldChannels != null && !oldChannels.getList().isEmpty(); + mPreferencesHelper.getNotificationChannels(pkg, uid, true, false); + final boolean hadNonBundleChannel = + oldChannels != null && !oldChannels.getList().isEmpty(); boolean needsPolicyFileChange = false; boolean hasRequestedNotificationPermission = false; for (int i = 0; i < channelsSize; i++) { @@ -4410,13 +4411,18 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false), NOTIFICATION_CHANNEL_OR_GROUP_ADDED); - boolean hasChannel = hadChannel || hasRequestedNotificationPermission; - if (!hasChannel) { + boolean hasNonBundleChannel = + hadNonBundleChannel || hasRequestedNotificationPermission; + if (!hasNonBundleChannel) { ParceledListSlice<NotificationChannel> currChannels = - mPreferencesHelper.getNotificationChannels(pkg, uid, true); - hasChannel = currChannels != null && !currChannels.getList().isEmpty(); - } - if (!hadChannel && hasChannel && !hasRequestedNotificationPermission + mPreferencesHelper.getNotificationChannels(pkg, uid, true, false); + hasNonBundleChannel = + currChannels != null && !currChannels.getList().isEmpty(); + } + // show perm prompt if new non-bundle channel added and the user has not + // seen the prompt + if (!hadNonBundleChannel && hasNonBundleChannel + && !hasRequestedNotificationPermission && startingTaskId != ActivityTaskManager.INVALID_TASK_ID) { hasRequestedNotificationPermission = true; if (mPermissionPolicyInternal == null) { @@ -4651,7 +4657,7 @@ public class NotificationManagerService extends SystemService { public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) { enforceSystemOrSystemUI("getNotificationChannelsForPackage"); - return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted); + return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted, true); } @Override @@ -4783,7 +4789,7 @@ public class NotificationManagerService extends SystemService { /* ignore */ } return mPreferencesHelper.getNotificationChannels( - targetPkg, targetUid, false /* includeDeleted */); + targetPkg, targetUid, false /* includeDeleted */, true); } throw new SecurityException("Pkg " + callingPkg + " cannot read channels for " + targetPkg + " in " + userId); @@ -6652,7 +6658,7 @@ public class NotificationManagerService extends SystemService { verifyPrivilegedListener(token, user, true); return mPreferencesHelper.getNotificationChannels(pkg, - getUidForPackageAndUser(pkg, user), false /* includeDeleted */); + getUidForPackageAndUser(pkg, user), false /* includeDeleted */, true); } @Override @@ -7693,8 +7699,9 @@ public class NotificationManagerService extends SystemService { } int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) { - return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted).getList() - .size(); + // don't show perm prompt if the only channels are bundle channels + return mPreferencesHelper.getNotificationChannels( + pkg, uid, includeDeleted, false).getList().size(); } void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid, @@ -8005,11 +8012,16 @@ public class NotificationManagerService extends SystemService { } /** - * Returns a channel, if exists, and restores deleted conversation channels. + * Returns a channel, if exists and is not a bundle channel, and restores deleted + * conversation channels. */ @Nullable private NotificationChannel getNotificationChannelRestoreDeleted(String pkg, int callingUid, int notificationUid, String channelId, String conversationId) { + if (SYSTEM_RESERVED_IDS.contains(channelId)) { + // apps cannot post to these channels directly, in case they post incorrect content + return null; + } // Restore a deleted conversation channel, if exists. Otherwise use the parent channel. NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel( pkg, notificationUid, channelId, conversationId, diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 3349b1308c3f..c9edc4106943 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -1870,7 +1870,7 @@ public class PreferencesHelper implements RankingConfig { @Override public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, - boolean includeDeleted) { + boolean includeDeleted, boolean includeBundles) { Objects.requireNonNull(pkg); List<NotificationChannel> channels = new ArrayList<>(); synchronized (mLock) { @@ -1882,7 +1882,9 @@ public class PreferencesHelper implements RankingConfig { for (int i = 0; i < N; i++) { final NotificationChannel nc = r.channels.valueAt(i); if (includeDeleted || !nc.isDeleted()) { - channels.add(nc); + if (includeBundles || !SYSTEM_RESERVED_IDS.contains(nc.getId())) { + channels.add(nc); + } } } return new ParceledListSlice<>(channels); diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index 8df24c9911a6..001e91cbea12 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -55,5 +55,5 @@ public interface RankingConfig { void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId); void permanentlyDeleteNotificationChannels(String pkg, int uid); ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, - boolean includeDeleted); + boolean includeDeleted, boolean includeBundles); } diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig index a24c743929b5..f79d9ef174ea 100644 --- a/services/core/java/com/android/server/notification/flags.aconfig +++ b/services/core/java/com/android/server/notification/flags.aconfig @@ -165,6 +165,13 @@ flag { } flag { + name: "notification_lock_screen_settings" + namespace: "systemui" + description: "This flag enables the new settings page for the notifications on lock screen." + bug: "367455695" +} + +flag { name: "notification_vibration_in_sound_uri" namespace: "systemui" description: "This flag enables sound uri with vibration source" diff --git a/services/core/java/com/android/server/pinner/PinnerService.java b/services/core/java/com/android/server/pinner/PinnerService.java index d7ac5203ff53..2c75926c4943 100644 --- a/services/core/java/com/android/server/pinner/PinnerService.java +++ b/services/core/java/com/android/server/pinner/PinnerService.java @@ -121,9 +121,6 @@ public final class PinnerService extends SystemService { private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean("pinner.use_pinlist", true); - private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app. - private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app. - public static final String ANON_REGION_STAT_NAME = "[anon]"; private static final String SYSTEM_GROUP_NAME = "system"; @@ -179,8 +176,10 @@ public final class PinnerService extends SystemService { // Resource-configured pinner flags; private final boolean mConfiguredToPinCamera; + private final int mConfiguredCameraPinBytes; private final int mConfiguredHomePinBytes; private final boolean mConfiguredToPinAssistant; + private final int mConfiguredAssistantPinBytes; private final int mConfiguredWebviewPinBytes; // This is the percentage of total device memory that will be used to set the global quota. @@ -250,6 +249,10 @@ public final class PinnerService extends SystemService { mDeviceConfigInterface = mInjector.getDeviceConfigInterface(); mConfiguredToPinCamera = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerCameraApp); + mConfiguredCameraPinBytes = context.getResources().getInteger( + com.android.internal.R.integer.config_pinnerCameraPinBytes); + mConfiguredAssistantPinBytes = context.getResources().getInteger( + com.android.internal.R.integer.config_pinnerAssistantPinBytes); mConfiguredHomePinBytes = context.getResources().getInteger( com.android.internal.R.integer.config_pinnerHomePinBytes); mConfiguredToPinAssistant = context.getResources().getBoolean( @@ -812,11 +815,11 @@ public final class PinnerService extends SystemService { private int getSizeLimitForKey(@AppKey int key) { switch (key) { case KEY_CAMERA: - return MAX_CAMERA_PIN_SIZE; + return mConfiguredCameraPinBytes; case KEY_HOME: return mConfiguredHomePinBytes; case KEY_ASSISTANT: - return MAX_ASSISTANT_PIN_SIZE; + return mConfiguredAssistantPinBytes; default: return 0; } @@ -1303,6 +1306,10 @@ public final class PinnerService extends SystemService { pw.format(" Maximum Pinner quota: %d bytes (%.2f MB)\n", mConfiguredMaxPinnedMemory, mConfiguredMaxPinnedMemory / bytesPerMB); pw.format(" Max Home App Pin Bytes (without deps): %d\n", mConfiguredHomePinBytes); + pw.format(" Max Assistant App Pin Bytes (without deps): %d\n", + mConfiguredAssistantPinBytes); + pw.format( + " Max Camera App Pin Bytes (without deps): %d\n", mConfiguredCameraPinBytes); pw.format("\nPinned Files:\n"); synchronized (PinnerService.this) { long totalSize = 0; diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java index 49a6ffde6783..a221152222ee 100644 --- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java +++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java @@ -55,6 +55,8 @@ public class BackgroundUserSoundNotifier { private static final String ACTION_SWITCH_USER = "com.android.server.ACTION_SWITCH_TO_USER"; private static final String ACTION_DISMISS_NOTIFICATION = "com.android.server.ACTION_DISMISS_NOTIFICATION"; + private static final String EXTRA_NOTIFICATION_CLIENT_UID = + "com.android.server.EXTRA_CLIENT_UID"; /** * The clientUid from the AudioFocusInfo of the background user, * for which an active notification is currently displayed. @@ -101,7 +103,7 @@ public class BackgroundUserSoundNotifier { ActivityManager am = mSystemUserContext.getSystemService(ActivityManager.class); registerReceiver(am); - mBgUserListener = new BackgroundUserListener(mSystemUserContext); + mBgUserListener = new BackgroundUserListener(); AudioPolicy.Builder focusControlPolicyBuilder = new AudioPolicy.Builder(mSystemUserContext); focusControlPolicyBuilder.setLooper(Looper.getMainLooper()); @@ -119,26 +121,16 @@ public class BackgroundUserSoundNotifier { final class BackgroundUserListener extends AudioPolicy.AudioPolicyFocusListener { - Context mSystemContext; - - BackgroundUserListener(Context systemContext) { - mSystemContext = systemContext; - } - - @SuppressLint("MissingPermission") public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) { try { - BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi, - mSystemContext.createContextAsUser( - UserHandle.of(ActivityManager.getCurrentUser()), 0)); + BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi); } catch (RemoteException e) { throw new RuntimeException(e); } } - @SuppressLint("MissingPermission") public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) { - BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary(); + BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary(afi.getClientUid()); } } @@ -160,7 +152,7 @@ public class BackgroundUserSoundNotifier { if (mNotificationClientUid == -1) { return; } - dismissNotification(); + dismissNotification(mNotificationClientUid); if (DEBUG) { final int actionIndex = intent.getAction().lastIndexOf(".") + 1; @@ -171,7 +163,7 @@ public class BackgroundUserSoundNotifier { } if (ACTION_MUTE_SOUND.equals(intent.getAction())) { - muteAlarmSounds(mSystemUserContext); + muteAlarmSounds(mNotificationClientUid); } else if (ACTION_SWITCH_USER.equals(intent.getAction())) { activityManager.switchUser(UserHandle.getUserId(mNotificationClientUid)); } @@ -193,17 +185,17 @@ public class BackgroundUserSoundNotifier { */ @SuppressLint("MissingPermission") @VisibleForTesting - void muteAlarmSounds(Context context) { - AudioManager audioManager = context.getSystemService(AudioManager.class); + void muteAlarmSounds(int notificationClientUid) { + AudioManager audioManager = mSystemUserContext.getSystemService(AudioManager.class); if (audioManager != null) { for (AudioPlaybackConfiguration apc : audioManager.getActivePlaybackConfigurations()) { - if (apc.getClientUid() == mNotificationClientUid && apc.getPlayerProxy() != null) { + if (apc.getClientUid() == notificationClientUid && apc.getPlayerProxy() != null) { apc.getPlayerProxy().stop(); } } } - AudioFocusInfo currentAfi = getAudioFocusInfoForNotification(); + AudioFocusInfo currentAfi = getAudioFocusInfoForNotification(notificationClientUid); if (currentAfi != null) { mFocusControlAudioPolicy.sendFocusLossAndUpdate(currentAfi); } @@ -212,9 +204,14 @@ public class BackgroundUserSoundNotifier { /** * Check if sound is coming from background user and show notification is required. */ + @SuppressLint("MissingPermission") @VisibleForTesting - void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi, Context foregroundContext) - throws RemoteException { + void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi) throws RemoteException { + if (afi == null) { + return; + } + Context foregroundContext = mSystemUserContext.createContextAsUser( + UserHandle.of(ActivityManager.getCurrentUser()), 0); final int userId = UserHandle.getUserId(afi.getClientUid()); final int usage = afi.getAttributes().getUsage(); UserInfo userInfo = mUserManager.getUserInfo(userId); @@ -232,8 +229,8 @@ public class BackgroundUserSoundNotifier { mNotificationClientUid = afi.getClientUid(); - mNotificationManager.notifyAsUser(LOG_TAG, mNotificationClientUid, - createNotification(userInfo.name, foregroundContext), + mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(), + createNotification(userInfo.name, foregroundContext, afi.getClientUid()), foregroundContext.getUser()); } } @@ -245,14 +242,15 @@ public class BackgroundUserSoundNotifier { * focus ownership. */ @VisibleForTesting - void dismissNotificationIfNecessary() { - if (getAudioFocusInfoForNotification() == null && mNotificationClientUid >= 0) { + void dismissNotificationIfNecessary(int notificationClientUid) { + if (getAudioFocusInfoForNotification(notificationClientUid) == null + && mNotificationClientUid >= 0) { if (DEBUG) { Log.d(LOG_TAG, "Alarm ringing on background user " - + UserHandle.getUserHandleForUid(mNotificationClientUid).getIdentifier() + + UserHandle.getUserHandleForUid(notificationClientUid).getIdentifier() + " left focus stack, dismissing notification"); } - dismissNotification(); + dismissNotification(notificationClientUid); mNotificationClientUid = -1; } } @@ -262,8 +260,8 @@ public class BackgroundUserSoundNotifier { * shown. */ @SuppressLint("MissingPermission") - private void dismissNotification() { - mNotificationManager.cancelAsUser(LOG_TAG, mNotificationClientUid, UserHandle.ALL); + private void dismissNotification(int notificationClientUid) { + mNotificationManager.cancelAsUser(LOG_TAG, notificationClientUid, UserHandle.ALL); } /** @@ -272,11 +270,11 @@ public class BackgroundUserSoundNotifier { @SuppressLint("MissingPermission") @VisibleForTesting @Nullable - AudioFocusInfo getAudioFocusInfoForNotification() { - if (mNotificationClientUid >= 0) { + AudioFocusInfo getAudioFocusInfoForNotification(int notificationClientUid) { + if (notificationClientUid >= 0) { List<AudioFocusInfo> stack = mFocusControlAudioPolicy.getFocusStack(); for (int i = stack.size() - 1; i >= 0; i--) { - if (stack.get(i).getClientUid() == mNotificationClientUid) { + if (stack.get(i).getClientUid() == notificationClientUid) { return stack.get(i); } } @@ -284,22 +282,24 @@ public class BackgroundUserSoundNotifier { return null; } - private PendingIntent createPendingIntent(String intentAction) { + private PendingIntent createPendingIntent(String intentAction, int notificationClientUid) { final Intent intent = new Intent(intentAction); - PendingIntent resultPI = PendingIntent.getBroadcast(mSystemUserContext, 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - return resultPI; + intent.putExtra(EXTRA_NOTIFICATION_CLIENT_UID, notificationClientUid); + return PendingIntent.getBroadcast(mSystemUserContext, notificationClientUid, intent, + PendingIntent.FLAG_IMMUTABLE); } + @SuppressLint("MissingPermission") @VisibleForTesting - Notification createNotification(String userName, Context fgContext) { + Notification createNotification(String userName, Context fgContext, int notificationClientUid) { final String title = fgContext.getString(R.string.bg_user_sound_notification_title_alarm, userName); final int icon = R.drawable.ic_audio_alarm; - PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND); - PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER); - PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION); + PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND, notificationClientUid); + PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER, notificationClientUid); + PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION, + notificationClientUid); final Notification.Action mute = new Notification.Action.Builder(null, fgContext.getString(R.string.bg_user_sound_notification_button_mute), diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java index 369029adac59..84a5f2b0e8bc 100644 --- a/services/core/java/com/android/server/pm/BroadcastHelper.java +++ b/services/core/java/com/android/server/pm/BroadcastHelper.java @@ -80,11 +80,6 @@ import java.util.function.BiFunction; */ public final class BroadcastHelper { private static final boolean DEBUG_BROADCASTS = false; - /** - * Permissions required in order to receive instant application lifecycle broadcasts. - */ - private static final String[] INSTANT_APP_BROADCAST_PERMISSION = - new String[]{android.Manifest.permission.ACCESS_INSTANT_APPS}; private final UserManagerInternal mUmInternal; private final ActivityManagerInternal mAmInternal; @@ -115,7 +110,7 @@ public final class BroadcastHelper { SparseArray<int[]> broadcastAllowList = new SparseArray<>(); broadcastAllowList.put(userId, visibilityAllowList); broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList, - filterExtrasForReceiver, bOptions); + filterExtrasForReceiver, bOptions, null /* requiredPermissions */); } void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, @@ -123,7 +118,7 @@ public final class BroadcastHelper { final int[] userIds, int[] instantUserIds, @Nullable SparseArray<int[]> broadcastAllowList, @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, - @Nullable Bundle bOptions) { + @Nullable Bundle bOptions, @Nullable String[] requiredPermissions) { try { final IActivityManager am = ActivityManager.getService(); if (am == null) return; @@ -137,12 +132,12 @@ public final class BroadcastHelper { if (ArrayUtils.isEmpty(instantUserIds)) { doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver, resolvedUserIds, false /* isInstantApp */, broadcastAllowList, - filterExtrasForReceiver, bOptions); + filterExtrasForReceiver, bOptions, requiredPermissions); } else { // send restricted broadcasts for instant apps doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver, - instantUserIds, true /* isInstantApp */, null, - null /* filterExtrasForReceiver */, bOptions); + instantUserIds, true /* isInstantApp */, null /* broadcastAllowList */, + null /* filterExtrasForReceiver */, bOptions, requiredPermissions); } } catch (RemoteException ex) { } @@ -166,7 +161,8 @@ public final class BroadcastHelper { boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList, @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, - @Nullable Bundle bOptions) { + @Nullable Bundle bOptions, + @Nullable String[] requiredPermissions) { for (int userId : userIds) { final Intent intent = new Intent(action, pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null); @@ -189,17 +185,18 @@ public final class BroadcastHelper { intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags); broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList, - filterExtrasForReceiver, bOptions); + filterExtrasForReceiver, bOptions, requiredPermissions); } } - private void broadcastIntent(Intent intent, IIntentReceiver finishedReceiver, boolean isInstantApp, int userId, @Nullable SparseArray<int[]> broadcastAllowList, @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, - @Nullable Bundle bOptions) { - final String[] requiredPermissions = - isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null; + @Nullable Bundle bOptions, @Nullable String[] requiredPermissions) { + if (isInstantApp) { + requiredPermissions = ArrayUtils.appendElement(String.class, requiredPermissions, + android.Manifest.permission.ACCESS_INSTANT_APPS); + } if (DEBUG_BROADCASTS) { RuntimeException here = new RuntimeException("here"); here.fillInStackTrace(); @@ -234,7 +231,7 @@ public final class BroadcastHelper { null /* instantUserIds */, null /* broadcastAllowList */, (callingUid, intentExtras) -> filterExtrasChangedPackageList( snapshot, callingUid, intentExtras), - null /* bOptions */); + null /* bOptions */, null /* requiredPermissions */); } /** @@ -294,14 +291,29 @@ public final class BroadcastHelper { return bOptions; } - private void sendPackageChangedBroadcast(@NonNull String packageName, - boolean dontKillApp, - @NonNull ArrayList<String> componentNames, - int packageUid, - @Nullable String reason, - @Nullable int[] userIds, - @Nullable int[] instantUserIds, - @Nullable SparseArray<int[]> broadcastAllowList) { + private void sendPackageChangedBroadcastInternal(@NonNull String packageName, + boolean dontKillApp, + @NonNull ArrayList<String> componentNames, + int packageUid, + @Nullable String reason, + @Nullable int[] userIds, + @Nullable int[] instantUserIds, + @Nullable SparseArray<int[]> broadcastAllowList) { + sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames, + packageUid, reason, userIds, instantUserIds, broadcastAllowList, + null /* targetPackageName */, null /* requiredPermissions */); + } + + private void sendPackageChangedBroadcastWithPermissions(@NonNull String packageName, + boolean dontKillApp, + @NonNull ArrayList<String> componentNames, + int packageUid, + @Nullable String reason, + @Nullable int[] userIds, + @Nullable int[] instantUserIds, + @Nullable SparseArray<int[]> broadcastAllowList, + @Nullable String targetPackageName, + @Nullable String[] requiredPermissions) { if (DEBUG_INSTALL) { Log.v(TAG, "Sending package changed: package=" + packageName + " components=" + componentNames); @@ -321,9 +333,10 @@ public final class BroadcastHelper { // little component state change. final int flags = !componentNames.contains(packageName) ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0; - sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null, - userIds, instantUserIds, broadcastAllowList, null /* filterExtrasForReceiver */, - null /* bOptions */); + sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, + targetPackageName, null /* finishedReceiver */, userIds, instantUserIds, + broadcastAllowList, null /* filterExtrasForReceiver */, null /* bOptions */, + requiredPermissions); } static void sendDeviceCustomizationReadyBroadcast() { @@ -680,7 +693,8 @@ public final class BroadcastHelper { sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0, null, null, userIds, instantUserIds, - broadcastAllowlist, null /* filterExtrasForReceiver */, null); + broadcastAllowlist, null /* filterExtrasForReceiver */, null /* bOptions */, + null /* requiredPermissions */); // Send to PermissionController for all new users, even if it may not be running for some // users if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) { @@ -688,7 +702,8 @@ public final class BroadcastHelper { packageName, extras, 0, mContext.getPackageManager().getPermissionControllerPackageName(), null, userIds, instantUserIds, - broadcastAllowlist, null /* filterExtrasForReceiver */, null); + broadcastAllowlist, null /* filterExtrasForReceiver */, null /* bOptions */, + null /* requiredPermissions */); } } @@ -719,7 +734,8 @@ public final class BroadcastHelper { int[] userIds, int[] instantUserIds) { sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0, installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */, - null /* filterExtrasForReceiver */, null); + null /* filterExtrasForReceiver */, null /* bOptions */, + null /* requiredPermissions */); } /** @@ -824,7 +840,7 @@ public final class BroadcastHelper { final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; final SparseArray<int[]> broadcastAllowList = isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds); - mHandler.post(() -> sendPackageChangedBroadcast( + mHandler.post(() -> sendPackageChangedBroadcastInternal( packageName, dontKillApp, componentNames, packageUid, reason, userIds, instantUserIds, broadcastAllowList)); mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames, @@ -843,7 +859,7 @@ public final class BroadcastHelper { @Nullable Bundle bOptions) { mHandler.post(() -> sendPackageBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList, - null /* filterExtrasForReceiver */, bOptions)); + null /* filterExtrasForReceiver */, bOptions, null /* requiredPermissions */)); if (targetPkg == null) { // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called // many times to different targets, e.g. installer app, permission controller, other @@ -1014,7 +1030,7 @@ public final class BroadcastHelper { extras, flags, null /* targetPkg */, null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */, filterExtrasForReceiver, - options)); + options, null /* requiredPermissions */)); notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */, filterExtrasForReceiver); } @@ -1046,9 +1062,12 @@ public final class BroadcastHelper { } else { intentExtras = null; } - doSendBroadcast(action, null, intentExtras, - Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null, - targetUserIds, false, null, null, null); + doSendBroadcast(action, null /* pkg */, intentExtras, + Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, + null /* finishedReceiver */, + targetUserIds, false /* isInstantApp */, null /* broadcastAllowList */, + null /* filterExtrasForReceiver */, null /* bOptions */, + null /* requiredPermissions */); } }); } @@ -1077,7 +1096,7 @@ public final class BroadcastHelper { null /* broadcastAllowList */, (callingUid, intentExtras) -> filterExtrasChangedPackageList( snapshot, callingUid, intentExtras), - null /* bOptions */)); + null /* bOptions */, null /* requiredPermissions */)); } void sendResourcesChangedBroadcastAndNotify(@NonNull Computer snapshot, diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 9e8598a04a98..0b58c759b284 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -791,16 +791,6 @@ public final class DexOptHelper { } try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); - - // This mirrors logic from commitReconciledScanResultLocked, where the library - // files needed for dexopt are assigned. - PackageSetting realPkgSetting = installRequest.getRealPackageSetting(); - // Unfortunately, the updated system app flag is only tracked on this - // PackageSetting - boolean isUpdatedSystemApp = - installRequest.getScannedPackageSetting().isUpdatedSystemApp(); - realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp); - DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService( installRequest, dexoptOptions); installRequest.onDexoptFinished(dexOptResult); diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 34d939b07187..f6a808b6c33e 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -25,12 +25,14 @@ import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVI import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED; import static android.content.pm.PackageInstaller.UNARCHIVAL_GENERIC_ERROR; import static android.content.pm.PackageInstaller.UNARCHIVAL_OK; +import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN; import static android.content.pm.PackageManager.DELETE_ARCHIVE; import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import static com.android.server.pm.PackageArchiver.isArchivingEnabled; +import static com.android.server.pm.PackageInstallerSession.isValidVerificationPolicy; import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -150,6 +152,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.IntPredicate; import java.util.function.Supplier; @@ -275,6 +278,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } }; + /** + * Default verification policy for incoming installation sessions. + * TODO(b/360129657): update the default policy. + */ + private final AtomicInteger mVerificationPolicy = new AtomicInteger( + VERIFICATION_POLICY_BLOCK_FAIL_WARN); + private static final class Lifecycle extends SystemService { private final PackageInstallerService mPackageInstallerService; @@ -1042,7 +1052,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid, null, null, false, false, false, false, null, SessionInfo.INVALID_ID, false, false, false, PackageManager.INSTALL_UNKNOWN, "", null, - mVerifierController); + mVerifierController, mVerificationPolicy.get()); synchronized (mSessions) { mSessions.put(sessionId, session); @@ -1866,6 +1876,34 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } + @Override + public @PackageInstaller.VerificationPolicy int getVerificationPolicy() { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need the " + + "com.android.permission.VERIFICATION_AGENT permission " + + "to get the verification policy"); + } + return mVerificationPolicy.get(); + } + + @Override + public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need the " + + "com.android.permission.VERIFICATION_AGENT permission " + + "to set the verification policy"); + } + if (!isValidVerificationPolicy(policy)) { + return false; + } + if (policy != mVerificationPolicy.get()) { + mVerificationPolicy.set(policy); + } + return true; + } + private static int getSessionCount(SparseArray<PackageInstallerSession> sessions, int installerUid) { int count = 0; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 9e0ba8492ab9..9a9e434c32d3 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -21,9 +21,17 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_INSTA import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO; import static android.content.pm.DataLoaderType.INCREMENTAL; import static android.content.pm.DataLoaderType.STREAMING; +import static android.content.pm.PackageInstaller.EXTRA_VERIFICATION_FAILURE_REASON; import static android.content.pm.PackageInstaller.LOCATION_DATA_APP; import static android.content.pm.PackageInstaller.UNARCHIVAL_OK; import static android.content.pm.PackageInstaller.UNARCHIVAL_STATUS_UNSET; +import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE; +import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED; +import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_UNKNOWN; +import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; +import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_OPEN; +import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN; +import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_NONE; import static android.content.pm.PackageItemInfo.MAX_SAFE_LABEL_LENGTH; import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE; @@ -38,7 +46,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_VERIFICATION_FAIL import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_STAGED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; -import static android.content.pm.verify.pkg.VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN; +import static android.content.pm.verify.pkg.VerificationSession.VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE; import static android.os.Process.INVALID_UID; import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; import static android.system.OsConstants.O_CREAT; @@ -226,7 +234,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; -import java.util.function.Supplier; public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String TAG = "PackageInstallerSession"; @@ -313,6 +320,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT = "applicationEnabledSettingPersistent"; private static final String ATTR_DOMAIN = "domain"; + private static final String ATTR_VERIFICATION_POLICY = "verificationPolicy"; private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT; @@ -410,6 +418,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private final PackageSessionProvider mSessionProvider; private final SilentUpdatePolicy mSilentUpdatePolicy; /** + * The verification policy applied to this session, which might be different from the default + * verification policy used by the system. + */ + private final AtomicInteger mVerificationPolicy; + /** * Note all calls must be done outside {@link #mLock} to prevent lock inversion. */ private final StagingManager mStagingManager; @@ -791,7 +804,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (errorMsg != null) { Slog.e(TAG, "verifySession error: " + errorMsg); setSessionFailed(INSTALL_FAILED_INTERNAL_ERROR, errorMsg); - onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, errorMsg); + onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, errorMsg, + /* extras= */ null); return false; } return true; @@ -1167,7 +1181,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int sessionErrorCode, String sessionErrorMessage, DomainSet preVerifiedDomains, - @NonNull VerifierController verifierController) { + @NonNull VerifierController verifierController, + @PackageInstaller.VerificationPolicy int verificationPolicy) { mCallback = callback; mContext = context; mPm = pm; @@ -1177,6 +1192,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mHandler = new Handler(looper, mHandlerCallback); mStagingManager = stagingManager; mVerifierController = verifierController; + mVerificationPolicy = new AtomicInteger(verificationPolicy); this.sessionId = sessionId; this.userId = userId; @@ -1262,9 +1278,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - if (Flags.verificationService()) { + if (shouldUseVerificationService()) { // Start binding to the verification service, if not bound already. - mVerifierController.bindToVerifierServiceIfNeeded(() -> pm.snapshotComputer(), userId); + mVerifierController.bindToVerifierServiceIfNeeded(mPm::snapshotComputer, userId); if (!TextUtils.isEmpty(params.appPackageName)) { mVerifierController.notifyPackageNameAvailable(params.appPackageName); } @@ -2580,10 +2596,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { dispatchSessionFinished(error, detailMessage, null); } - private void onSessionVerificationFailure(int error, String msg) { + private void onSessionVerificationFailure(int error, String msg, Bundle extras) { Slog.e(TAG, "Failed to verify session " + sessionId); // Dispatch message to remove session from PackageInstallerService. - dispatchSessionFinished(error, msg, null); + dispatchSessionFinished(error, msg, extras); maybeFinishChildSessions(error, msg); } @@ -2856,38 +2872,60 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final String completeMsg = ExceptionUtils.getCompleteMessage(e); final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg); setSessionFailed(e.error, errorMsg); - onSessionVerificationFailure(e.error, errorMsg); - } - if (Flags.verificationService()) { - final Supplier<Computer> snapshotSupplier = mPm::snapshotComputer; - if (mVerifierController.isVerifierInstalled(snapshotSupplier, userId)) { - final SigningInfo signingInfo; - final List<SharedLibraryInfo> declaredLibraries; - synchronized (mLock) { - signingInfo = new SigningInfo(mSigningDetails); - declaredLibraries = - mPackageLite == null ? null : mPackageLite.getDeclaredLibraries(); - } - // Send the request to the verifier and wait for its response before the rest of - // the installation can proceed. - if (!mVerifierController.startVerificationSession(snapshotSupplier, userId, - sessionId, getPackageName(), Uri.fromFile(stageDir), signingInfo, - declaredLibraries, /* extensionParams= */ null, - new VerifierCallback(), /* retry= */ false)) { - // A verifier is installed but cannot be connected. Installation disallowed. - onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, - "A verifier agent is available on device but cannot be connected."); - } - } else { - // Verifier is not installed. Let the installation pass for now. - resumeVerify(); + onSessionVerificationFailure(e.error, errorMsg, /* extras= */ null); + } + if (shouldUseVerificationService()) { + final SigningInfo signingInfo; + final List<SharedLibraryInfo> declaredLibraries; + synchronized (mLock) { + signingInfo = new SigningInfo(mSigningDetails); + declaredLibraries = + mPackageLite == null ? null : mPackageLite.getDeclaredLibraries(); + } + // Send the request to the verifier and wait for its response before the rest of + // the installation can proceed. + if (!mVerifierController.startVerificationSession(mPm::snapshotComputer, userId, + sessionId, getPackageName(), Uri.fromFile(stageDir), signingInfo, + declaredLibraries, mVerificationPolicy.get(), /* extensionParams= */ null, + new VerifierCallback(), /* retry= */ false)) { + // A verifier is installed but cannot be connected. Installation disallowed. + onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, + "A verifier agent is available on device but cannot be connected.", + /* extras= */ null); } } else { - // New verification feature is not enabled. Proceed to the rest of the verification. + // No need to check with verifier. Proceed with the rest of the verification. resumeVerify(); } } + private boolean shouldUseVerificationService() { + if (!Flags.verificationService()) { + // Feature is not enabled. + return false; + } + if ((params.installFlags & PackageManager.INSTALL_FROM_ADB) != 0) { + // adb installs are exempted from verification unless explicitly requested + if (!params.forceVerification) { + return false; + } + } + final String verifierPackageName = mVerifierController.getVerifierPackageName( + mPm::snapshotComputer, userId); + if (verifierPackageName == null) { + // Feature is enabled but no verifier installed. + return false; + } + synchronized (mLock) { + if (verifierPackageName.equals(mPackageName)) { + // The verifier itself is being updated. Skip. + Slog.w(TAG, "Skipping verification service because the verifier is being updated"); + return false; + } + } + return true; + } + private void resumeVerify() { if (mVerificationInProgress) { Slog.w(TAG, "Verification is already in progress for session " + sessionId); @@ -2917,7 +2955,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final String completeMsg = ExceptionUtils.getCompleteMessage(e); final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg); setSessionFailed(e.error, errorMsg); - onSessionVerificationFailure(e.error, errorMsg); + onSessionVerificationFailure(e.error, errorMsg, /* extras= */ null); } } @@ -2926,24 +2964,57 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { */ public class VerifierCallback { /** + * Called by the VerifierController when the verifier requests to get the current + * verification policy for this session. + */ + public @PackageInstaller.VerificationPolicy int getVerificationPolicy() { + return mVerificationPolicy.get(); + } + /** + * Called by the VerifierController when the verifier requests to change the verification + * policy for this session. + */ + public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) { + if (!isValidVerificationPolicy(policy)) { + return false; + } + mVerificationPolicy.set(policy); + return true; + } + /** * Called by the VerifierController when the connection has failed. */ public void onConnectionFailed() { - mHandler.post(() -> { - onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, - "A verifier agent is available on device but cannot be connected."); - }); + // TODO(b/360129657): prompt user on fail warning + handleNonPackageBlockedFailure( + /* onFailWarning= */ PackageInstallerSession.this::resumeVerify, + /* onFailClosed= */ () -> { + Bundle bundle = new Bundle(); + bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON, + VERIFICATION_FAILED_REASON_UNKNOWN); + onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, + "A verifier agent is available on device but cannot be connected.", + bundle); + + }); } /** * Called by the VerifierController when the verification request has timed out. */ public void onTimeout() { - mHandler.post(() -> { - mVerifierController.notifyVerificationTimeout(sessionId); - onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, - "Verification timed out; missing a response from the verifier within the" - + " time limit"); - }); + // Always notify the verifier, regardless of the policy. + mVerifierController.notifyVerificationTimeout(sessionId); + // TODO(b/360129657): prompt user on fail warning + handleNonPackageBlockedFailure( + /* onFailWarning= */ PackageInstallerSession.this::resumeVerify, + /* onFailClosed= */ () -> { + Bundle bundle = new Bundle(); + bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON, + VERIFICATION_FAILED_REASON_UNKNOWN); + onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, + "Verification timed out; missing a response from the verifier" + + " within the time limit", bundle); + }); } /** * Called by the VerifierController when the verification request has received a complete @@ -2953,17 +3024,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Nullable PersistableBundle extensionResponse) { // TODO: handle extension response mHandler.post(() -> { - if (statusReceived.isVerified()) { + if (statusReceived.isVerified() + || mVerificationPolicy.get() == VERIFICATION_POLICY_NONE) { // Continue with the rest of the verification and installation. resumeVerify(); - } else { - StringBuilder sb = new StringBuilder("Verifier rejected the installation"); - if (!TextUtils.isEmpty(statusReceived.getFailureMessage())) { - sb.append(" with message: ").append(statusReceived.getFailureMessage()); - } - onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, - sb.toString()); + return; } + // Package is blocked. + StringBuilder sb = new StringBuilder("Verifier rejected the installation"); + if (!TextUtils.isEmpty(statusReceived.getFailureMessage())) { + sb.append(" with message: ").append(statusReceived.getFailureMessage()); + } + Bundle bundle = new Bundle(); + bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON, + VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED); + onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, + sb.toString(), bundle); }); } /** @@ -2971,16 +3047,51 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * response. */ public void onVerificationIncompleteReceived(int incompleteReason) { - mHandler.post(() -> { - if (incompleteReason == VERIFICATION_INCOMPLETE_UNKNOWN) { - // TODO: change this to a user confirmation and handle other incomplete reasons - onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, - "Verification cannot be completed for unknown reasons."); + // TODO(b/360129657): prompt user on fail warning + handleNonPackageBlockedFailure( + /* onFailWarning= */ PackageInstallerSession.this::resumeVerify, + /* onFailClosed= */ () -> { + final int failureReason; + StringBuilder sb = new StringBuilder( + "Verification cannot be completed because of "); + if (incompleteReason == VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE) { + failureReason = VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE; + sb.append("unavailable network."); + } else { + failureReason = VERIFICATION_FAILED_REASON_UNKNOWN; + sb.append("unknown reasons."); + } + Bundle bundle = new Bundle(); + bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON, failureReason); + onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, + sb.toString(), bundle); + }); + } + + private void handleNonPackageBlockedFailure(Runnable onFailWarning, Runnable onFailClosed) { + final Runnable r = switch (mVerificationPolicy.get()) { + case VERIFICATION_POLICY_NONE, VERIFICATION_POLICY_BLOCK_FAIL_OPEN -> + PackageInstallerSession.this::resumeVerify; + case VERIFICATION_POLICY_BLOCK_FAIL_WARN -> onFailWarning; + case VERIFICATION_POLICY_BLOCK_FAIL_CLOSED -> onFailClosed; + default -> { + Log.wtf(TAG, "Unknown verification policy: " + mVerificationPolicy.get()); + yield onFailClosed; } - }); + }; + mHandler.post(r); } } + /** + * Returns whether a policy is a valid verification policy. + */ + public static boolean isValidVerificationPolicy( + @PackageInstaller.VerificationPolicy int policy) { + return policy >= VERIFICATION_POLICY_NONE + && policy <= VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; + } + private IntentSender getRemoteStatusReceiver() { synchronized (mLock) { return mRemoteStatusReceiver; @@ -3156,7 +3267,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (error == INSTALL_SUCCEEDED) { onVerificationComplete(); } else { - onSessionVerificationFailure(error, msg); + onSessionVerificationFailure(error, msg, /* extras= */ null); } }); }); @@ -5328,6 +5439,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + /** + * @return the current policy for the verification request associated with this session. + */ + @VisibleForTesting + public @PackageInstaller.VerificationPolicy int getVerificationPolicy() { + assertCallerIsOwnerOrRoot(); + return mVerificationPolicy.get(); + } void setSessionReady() { synchronized (mLock) { @@ -5498,7 +5617,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } catch (InstallerException ignored) { } - if (Flags.verificationService() + if (shouldUseVerificationService() && !TextUtils.isEmpty(params.appPackageName) && !isCommitted()) { // Only notify for the cancellation if the verification request has not @@ -5631,6 +5750,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (!ArrayUtils.isEmpty(warnings)) { fillIn.putStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS, warnings); } + if (extras.containsKey(EXTRA_VERIFICATION_FAILURE_REASON)) { + fillIn.putExtra(EXTRA_VERIFICATION_FAILURE_REASON, + extras.getInt(EXTRA_VERIFICATION_FAILURE_REASON)); + } } try { final BroadcastOptions options = BroadcastOptions.makeBasic(); @@ -5786,6 +5909,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { out.attributeInt(null, ATTR_INSTALL_REASON, params.installReason); writeBooleanAttribute(out, ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT, params.applicationEnabledSettingPersistent); + out.attributeInt(null, ATTR_VERIFICATION_POLICY, mVerificationPolicy.get()); final boolean isDataLoader = params.dataLoaderParams != null; writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader); @@ -5936,6 +6060,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean sealed = in.getAttributeBoolean(null, ATTR_SEALED, false); final int parentSessionId = in.getAttributeInt(null, ATTR_PARENT_SESSION_ID, SessionInfo.INVALID_ID); + final int verificationPolicy = in.getAttributeInt(null, ATTR_VERIFICATION_POLICY, + VERIFICATION_POLICY_NONE); final SessionParams params = new SessionParams( SessionParams.MODE_INVALID); @@ -6110,6 +6236,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { installerUid, installSource, params, createdMillis, committedMillis, stageDir, stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied, - sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController); + sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController, + verificationPolicy); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 455776993c56..d78f12217271 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4709,10 +4709,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime()); mHandler.post(() -> { mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTOPPED, - packageName, extras, - Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, - userIds, null, broadcastAllowList, null, - null); + packageName, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, + null /* targetPkg */, null /* finishedReceiver */, userIds, + null /* instantUserIds */, broadcastAllowList, + null /* filterExtrasForReceiver */, null /* bOptions */, + null /* requiredPermissions */); }); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_UNSTOPPED, packageName, extras, userIds, null /* instantUserIds */, @@ -7169,17 +7170,17 @@ public class PackageManagerService implements PackageSender, TestUtilityService // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED mHandler.post(() -> { mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED, - packageName, extras, - flags, null, null, - userIds, null, broadcastAllowList, null, - null); + packageName, extras, flags, null /* targetPkg */, + null /* finishedReceiver */, userIds, null /* instantUserIds */, + broadcastAllowList, null /* filterExtrasForReceiver */, + null /* bOptions */, null /* requiredPermissions */); }); } else { mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED, - packageName, extras, - flags, null, null, - userIds, null, broadcastAllowList, null, - null); + packageName, extras, flags, null /* targetPkg */, + null /* finishedReceiver */, userIds, null /* instantUserIds */, + broadcastAllowList, null /* filterExtrasForReceiver */, null /* bOptions */, + null /* requiredPermissions */); } mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_RESTARTED, packageName, extras, userIds, null /* instantUserIds */, diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 7a53fe78c1bf..4652c3af1185 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -3599,6 +3599,9 @@ class PackageManagerShellCommand extends ShellCommand { .setCompilerFilter(sessionParams.dexoptCompilerFilter) .build(); break; + case "--force-verification": + sessionParams.setForceVerification(); + break; default: throw new IllegalArgumentException("Unknown option " + opt); } @@ -4805,6 +4808,7 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" https://source.android.com/docs/core/runtime/configure" + "#compiler_filters"); pw.println(" or 'skip'"); + pw.println(" --force-verification: if set, enable the verification for this install"); pw.println(""); pw.println(" install-existing [--user USER_ID|all|current]"); pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE"); diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerificationStatusTracker.java b/services/core/java/com/android/server/pm/verify/pkg/VerificationStatusTracker.java index db747f9940a0..67ac2a7572d8 100644 --- a/services/core/java/com/android/server/pm/verify/pkg/VerificationStatusTracker.java +++ b/services/core/java/com/android/server/pm/verify/pkg/VerificationStatusTracker.java @@ -30,9 +30,6 @@ public final class VerificationStatusTracker { private final @CurrentTimeMillisLong long mMaxTimeoutTime; @NonNull private final VerifierController.Injector mInjector; - // Record the package name associated with the verification result - @NonNull - private final String mPackageName; /** * By default, the timeout time is the default timeout duration plus the current time (when @@ -41,10 +38,8 @@ public final class VerificationStatusTracker { * can be extended via {@link #extendTimeRemaining} to the maximum allowed. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) - public VerificationStatusTracker(@NonNull String packageName, - long defaultTimeoutMillis, long maxExtendedTimeoutMillis, + public VerificationStatusTracker(long defaultTimeoutMillis, long maxExtendedTimeoutMillis, @NonNull VerifierController.Injector injector) { - mPackageName = packageName; mStartTime = injector.getCurrentTimeMillis(); mTimeoutTime = mStartTime + defaultTimeoutMillis; mMaxTimeoutTime = mStartTime + maxExtendedTimeoutMillis; @@ -93,9 +88,4 @@ public final class VerificationStatusTracker { public boolean isTimeout() { return mInjector.getCurrentTimeMillis() >= mTimeoutTime; } - - @NonNull - public String getPackageName() { - return mPackageName; - } } diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java index 7eac940933c2..a35618b309bb 100644 --- a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java +++ b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java @@ -30,11 +30,11 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningInfo; -import android.content.pm.verify.pkg.IVerificationSessionCallback; import android.content.pm.verify.pkg.IVerificationSessionInterface; import android.content.pm.verify.pkg.IVerifierService; import android.content.pm.verify.pkg.VerificationSession; @@ -44,7 +44,6 @@ import android.os.Build; import android.os.Handler; import android.os.PersistableBundle; import android.os.Process; -import android.os.RemoteException; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.Pair; @@ -94,9 +93,22 @@ public class VerifierController { // Max duration allowed to wait for a verifier to respond to a verification request. private static final long DEFAULT_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10); + /** + * Configurable maximum amount of time in milliseconds for the system to wait from the moment + * when the installation session requires a verification, till when the request is delivered to + * the verifier, pending the connection to be established. If the request has not been delivered + * to the verifier within this amount of time, e.g., because the verifier has crashed or ANR'd, + * the controller then sends a failure status back to the installation session. + * Flag type: {@code long} + * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE + */ + private static final String PROPERTY_VERIFIER_CONNECTION_TIMEOUT_MILLIS = + "verifier_connection_timeout_millis"; // The maximum amount of time to wait from the moment when the session requires a verification, // till when the request is delivered to the verifier, pending the connection to be established. - private static final long CONNECTION_TIMEOUT_SECONDS = 10; + private static final long DEFAULT_VERIFIER_CONNECTION_TIMEOUT_MILLIS = + TimeUnit.SECONDS.toMillis(10); + // The maximum amount of time to wait before the system unbinds from the verifier. private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6); @@ -127,15 +139,16 @@ public class VerifierController { } /** - * Used by the installation session to check if a verifier is installed. + * Used by the installation session to get the package name of the installed verifier. */ - public boolean isVerifierInstalled(Supplier<Computer> snapshotSupplier, int userId) { + @Nullable + public String getVerifierPackageName(Supplier<Computer> snapshotSupplier, int userId) { if (isVerifierConnected()) { // Verifier is connected or is being connected, so it must be installed. - return true; + return mRemoteServiceComponentName.getPackageName(); } // Verifier has been disconnected, or it hasn't been connected. Check if it's installed. - return mInjector.isVerifierInstalled(snapshotSupplier.get(), userId); + return mInjector.getVerifierPackageName(snapshotSupplier.get(), userId); } /** @@ -271,6 +284,7 @@ public class VerifierController { int installationSessionId, String packageName, Uri stagedPackageUri, SigningInfo signingInfo, List<SharedLibraryInfo> declaredLibraries, + @PackageInstaller.VerificationPolicy int verificationPolicy, PersistableBundle extensionParams, PackageInstallerSession.VerifierCallback callback, boolean retry) { // Try connecting to the verifier if not already connected @@ -292,8 +306,7 @@ public class VerifierController { /* id= */ verificationId, /* installSessionId= */ installationSessionId, packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams, - new VerificationSessionInterface(), - new VerificationSessionCallback(callback)); + verificationPolicy, new VerificationSessionInterface(callback)); AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> { if (!retry) { if (DEBUG) { @@ -306,7 +319,8 @@ public class VerifierController { } service.onVerificationRetry(session); } - }).orTimeout(CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS).whenComplete((res, err) -> { + }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS) + .whenComplete((res, err) -> { if (err != null) { Slog.e(TAG, "Error notifying verification request for session " + verificationId, err); @@ -318,7 +332,7 @@ public class VerifierController { final long defaultTimeoutMillis = mInjector.getVerificationRequestTimeoutMillis(); final long maxExtendedTimeoutMillis = mInjector.getMaxVerificationExtendedTimeoutMillis(); final VerificationStatusTracker tracker = new VerificationStatusTracker( - packageName, defaultTimeoutMillis, maxExtendedTimeoutMillis, mInjector); + defaultTimeoutMillis, maxExtendedTimeoutMillis, mInjector); synchronized (mVerificationStatus) { mVerificationStatus.put(verificationId, tracker); } @@ -407,6 +421,12 @@ public class VerifierController { // This class handles requests from the remote verifier private class VerificationSessionInterface extends IVerificationSessionInterface.Stub { + private final PackageInstallerSession.VerifierCallback mCallback; + + VerificationSessionInterface(PackageInstallerSession.VerifierCallback callback) { + mCallback = callback; + } + @Override public long getTimeoutTime(int verificationId) { checkCallerPermission(); @@ -432,17 +452,23 @@ public class VerifierController { return tracker.extendTimeRemaining(additionalMs); } } - } - private class VerificationSessionCallback extends IVerificationSessionCallback.Stub { - private final PackageInstallerSession.VerifierCallback mCallback; - - VerificationSessionCallback(PackageInstallerSession.VerifierCallback callback) { - mCallback = callback; + @Override + public boolean setVerificationPolicy(int verificationId, + @PackageInstaller.VerificationPolicy int policy) { + checkCallerPermission(); + synchronized (mVerificationStatus) { + final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId); + if (tracker == null) { + throw new IllegalStateException("Verification session " + verificationId + + " doesn't exist or has finished"); + } + } + return mCallback.setVerificationPolicy(policy); } @Override - public void reportVerificationIncomplete(int id, int reason) throws RemoteException { + public void reportVerificationIncomplete(int id, int reason) { checkCallerPermission(); final VerificationStatusTracker tracker; synchronized (mVerificationStatus) { @@ -451,23 +477,21 @@ public class VerifierController { throw new IllegalStateException("Verification session " + id + " doesn't exist or has finished"); } - mCallback.onVerificationIncompleteReceived(reason); } + mCallback.onVerificationIncompleteReceived(reason); // Remove status tracking and stop the timeout countdown removeStatusTracker(id); } @Override - public void reportVerificationComplete(int id, VerificationStatus verificationStatus) - throws RemoteException { + public void reportVerificationComplete(int id, VerificationStatus verificationStatus) { reportVerificationCompleteWithExtensionResponse(id, verificationStatus, /* extensionResponse= */ null); } @Override public void reportVerificationCompleteWithExtensionResponse(int id, - VerificationStatus verificationStatus, PersistableBundle extensionResponse) - throws RemoteException { + VerificationStatus verificationStatus, PersistableBundle extensionResponse) { checkCallerPermission(); final VerificationStatusTracker tracker; synchronized (mVerificationStatus) { @@ -519,10 +543,15 @@ public class VerifierController { } /** - * Check if a verifier is installed on this device. + * Return the package name of the verifier installed on this device. */ - public boolean isVerifierInstalled(Computer snapshot, int userId) { - return resolveVerifierComponentName(snapshot, userId) != null; + @Nullable + public String getVerifierPackageName(Computer snapshot, int userId) { + final ComponentName componentName = resolveVerifierComponentName(snapshot, userId); + if (componentName == null) { + return null; + } + return componentName.getPackageName(); } /** @@ -630,6 +659,14 @@ public class VerifierController { return getMaxVerificationExtendedTimeoutMillisFromDeviceConfig(); } + /** + * This is added so that we can mock the maximum connection timeout duration without + * calling into DeviceConfig. + */ + public long getVerifierConnectionTimeoutMillis() { + return getVerifierConnectionTimeoutMillisFromDeviceConfig(); + } + private static long getVerificationRequestTimeoutMillisFromDeviceConfig() { return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE, PROPERTY_VERIFICATION_REQUEST_TIMEOUT_MILLIS, @@ -641,5 +678,11 @@ public class VerifierController { PROPERTY_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS, DEFAULT_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS); } + + private static long getVerifierConnectionTimeoutMillisFromDeviceConfig() { + return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE, + PROPERTY_VERIFIER_CONNECTION_TIMEOUT_MILLIS, + DEFAULT_VERIFIER_CONNECTION_TIMEOUT_MILLIS); + } } } diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index eb62b5631c43..8ba56c5320f2 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -771,6 +771,10 @@ public class Notifier { public void onGroupRemoved(int groupId) { mInteractivityByGroupId.remove(groupId); mWakefulnessSessionObserver.removePowerGroup(groupId); + if (mFlags.isPerDisplayWakeByTouchEnabled()) { + resetDisplayInteractivities(); + mInputManagerInternal.setDisplayInteractivities(mDisplayInteractivities); + } } /** diff --git a/services/core/java/com/android/server/power/stats/WakelockStatsFrameworkEvents.java b/services/core/java/com/android/server/power/stats/WakelockStatsFrameworkEvents.java index c9693bd20a08..f387feca05f2 100644 --- a/services/core/java/com/android/server/power/stats/WakelockStatsFrameworkEvents.java +++ b/services/core/java/com/android/server/power/stats/WakelockStatsFrameworkEvents.java @@ -150,26 +150,14 @@ public class WakelockStatsFrameworkEvents { } @VisibleForTesting - public boolean inOverflow() { - synchronized (mLock) { - return inOverflowLocked(); - } - } - @GuardedBy("mLock") - private boolean inOverflowLocked() { + public boolean inOverflow() { return mWakeLockStats.size() >= SUMMARY_THRESHOLD; } @VisibleForTesting - public boolean inHardCap() { - synchronized (mLock) { - return inHardCapLocked(); - } - } - @GuardedBy("mLock") - private boolean inHardCapLocked() { + public boolean inHardCap() { return mWakeLockStats.size() >= MAX_WAKELOCK_DIMENSIONS; } @@ -189,9 +177,9 @@ public class WakelockStatsFrameworkEvents { long wakeLockDur = eventUptimeMillis - data.acquireUptimeMillis; // Rewrite key if in an overflow state. - if (inOverflowLocked() && !mWakeLockStats.containsKey(key)) { + if (inOverflow() && !mWakeLockStats.containsKey(key)) { key.setOverflow(); - if (inHardCapLocked() && !mWakeLockStats.containsKey(key)) { + if (inHardCap() && !mWakeLockStats.containsKey(key)) { key.setHardCap(); } } @@ -207,12 +195,41 @@ public class WakelockStatsFrameworkEvents { } } - public List<StatsEvent> pullFrameworkWakelockInfoAtoms() { - return pullFrameworkWakelockInfoAtoms(SystemClock.uptimeMillis()); + // Shim interface for testing. + @VisibleForTesting + public interface EventLogger { + void logResult( + int uid, String tag, int wakeLockLevel, long uptimeMillis, long completedCount); } - public List<StatsEvent> pullFrameworkWakelockInfoAtoms(long nowMillis) { + public List<StatsEvent> pullFrameworkWakelockInfoAtoms() { List<StatsEvent> result = new ArrayList<>(); + EventLogger logger = + new EventLogger() { + public void logResult( + int uid, + String tag, + int wakeLockLevel, + long uptimeMillis, + long completedCount) { + StatsEvent event = + StatsEvent.newBuilder() + .setAtomId(FrameworkStatsLog.FRAMEWORK_WAKELOCK_INFO) + .writeInt(uid) + .writeString(tag) + .writeInt(wakeLockLevel) + .writeLong(uptimeMillis) + .writeLong(completedCount) + .build(); + result.add(event); + } + }; + pullFrameworkWakelockInfoAtoms(SystemClock.uptimeMillis(), logger); + return result; + } + + @VisibleForTesting + public void pullFrameworkWakelockInfoAtoms(long nowMillis, EventLogger logger) { HashSet<WakeLockKey> keys = new HashSet<>(); // Used to collect open WakeLocks when in an overflow state. @@ -223,13 +240,13 @@ public class WakelockStatsFrameworkEvents { // If we are in an overflow state, an open wakelock may have a new key // that needs to be summarized. - if (inOverflowLocked()) { + if (inOverflow()) { for (WakeLockKey key : mOpenWakeLocks.keySet()) { if (!mWakeLockStats.containsKey(key)) { WakeLockData data = mOpenWakeLocks.get(key); key.setOverflow(); - if (inHardCapLocked() && !mWakeLockStats.containsKey(key)) { + if (inHardCap() && !mWakeLockStats.containsKey(key)) { key.setHardCap(); } keys.add(key); @@ -257,20 +274,14 @@ public class WakelockStatsFrameworkEvents { stats.uptimeMillis += openWakeLockUptime + extraTime.uptimeMillis; - StatsEvent event = - StatsEvent.newBuilder() - .setAtomId(FrameworkStatsLog.FRAMEWORK_WAKELOCK_INFO) - .writeInt(key.getUid()) - .writeString(key.getTag()) - .writeInt(key.getPowerManagerWakeLockLevel()) - .writeLong(stats.uptimeMillis) - .writeLong(stats.completedCount) - .build(); - result.add(event); + logger.logResult( + key.getUid(), + key.getTag(), + key.getPowerManagerWakeLockLevel(), + stats.uptimeMillis, + stats.completedCount); } } - - return result; } private static final String TAG = "BatteryStatsPulledMetrics"; diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java index 8f603fc34b32..075a31f3b24c 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java @@ -191,8 +191,7 @@ public class RotationResolverManagerService extends SensorPrivacyManager.Sensors.CAMERA); if (mIsServiceEnabled && isCameraAvailable) { final RotationResolverManagerPerUserService service = - getServiceForUserLocked( - UserHandle.getCallingUserId()); + getServiceForUserLocked(UserHandle.USER_CURRENT); final RotationResolutionRequest request; if (packageName == null) { request = new RotationResolutionRequest(/* packageName */ "", diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java new file mode 100644 index 000000000000..9a63c823d2d7 --- /dev/null +++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.advancedprotection; + +import static android.provider.Settings.Secure.ADVANCED_PROTECTION_MODE; + +import android.Manifest; +import android.annotation.EnforcePermission; +import android.annotation.NonNull; +import android.content.Context; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.PermissionEnforcer; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; +import android.provider.Settings; +import android.security.advancedprotection.IAdvancedProtectionCallback; +import android.security.advancedprotection.IAdvancedProtectionService; +import android.util.ArrayMap; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.FgThread; +import com.android.server.LocalServices; +import com.android.server.SystemService; +import com.android.server.pm.UserManagerInternal; + +import java.io.FileDescriptor; +import java.util.ArrayList; + +/** @hide */ +public class AdvancedProtectionService extends IAdvancedProtectionService.Stub { + private static final int MODE_CHANGED = 0; + private static final int CALLBACK_ADDED = 1; + + private final Handler mHandler; + private final AdvancedProtectionStore mStore; + private final ArrayMap<IBinder, IAdvancedProtectionCallback> mCallbacks = new ArrayMap<>(); + + private AdvancedProtectionService(@NonNull Context context) { + super(PermissionEnforcer.fromContext(context)); + mHandler = new AdvancedProtectionHandler(FgThread.get().getLooper()); + mStore = new AdvancedProtectionStore(context); + } + + @VisibleForTesting + AdvancedProtectionService(@NonNull Context context, @NonNull AdvancedProtectionStore store, + @NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer) { + super(permissionEnforcer); + mStore = store; + mHandler = new AdvancedProtectionHandler(looper); + } + + @Override + @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) + public boolean isAdvancedProtectionEnabled() { + isAdvancedProtectionEnabled_enforcePermission(); + final long identity = Binder.clearCallingIdentity(); + try { + return isAdvancedProtectionEnabledInternal(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + // Without permission check + private boolean isAdvancedProtectionEnabledInternal() { + return mStore.retrieve(); + } + + @Override + @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) + public void registerAdvancedProtectionCallback(@NonNull IAdvancedProtectionCallback callback) + throws RemoteException { + registerAdvancedProtectionCallback_enforcePermission(); + IBinder b = callback.asBinder(); + b.linkToDeath(new DeathRecipient(b), 0); + synchronized (mCallbacks) { + mCallbacks.put(b, callback); + sendCallbackAdded(isAdvancedProtectionEnabledInternal(), callback); + } + } + + @Override + @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) + public void unregisterAdvancedProtectionCallback(@NonNull IAdvancedProtectionCallback callback) + throws RemoteException { + unregisterAdvancedProtectionCallback_enforcePermission(); + synchronized (mCallbacks) { + mCallbacks.remove(callback.asBinder()); + } + } + + @Override + @EnforcePermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE) + public void setAdvancedProtectionEnabled(boolean enabled) { + setAdvancedProtectionEnabled_enforcePermission(); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mCallbacks) { + if (enabled != isAdvancedProtectionEnabledInternal()) { + mStore.store(enabled); + sendModeChanged(enabled); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, + FileDescriptor err, @NonNull String[] args, ShellCallback callback, + @NonNull ResultReceiver resultReceiver) { + (new AdvancedProtectionShellCommand(this)) + .exec(this, in, out, err, args, callback, resultReceiver); + } + + void sendModeChanged(boolean enabled) { + Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused */ -1) + .sendToTarget(); + } + + void sendCallbackAdded(boolean enabled, IAdvancedProtectionCallback callback) { + Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused*/ -1, + /*callback*/ callback) + .sendToTarget(); + } + + public static final class Lifecycle extends SystemService { + private final AdvancedProtectionService mService; + + public Lifecycle(@NonNull Context context) { + super(context); + mService = new AdvancedProtectionService(context); + } + + @Override + public void onStart() { + publishBinderService(Context.ADVANCED_PROTECTION_SERVICE, mService); + } + } + + @VisibleForTesting + static class AdvancedProtectionStore { + private final Context mContext; + private static final int APM_ON = 1; + private static final int APM_OFF = 0; + private final UserManagerInternal mUserManager; + + AdvancedProtectionStore(@NonNull Context context) { + mContext = context; + mUserManager = LocalServices.getService(UserManagerInternal.class); + } + + void store(boolean enabled) { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + ADVANCED_PROTECTION_MODE, enabled ? APM_ON : APM_OFF, + mUserManager.getMainUserId()); + } + + boolean retrieve() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + ADVANCED_PROTECTION_MODE, APM_OFF, mUserManager.getMainUserId()) == APM_ON; + } + } + + private class AdvancedProtectionHandler extends Handler { + private AdvancedProtectionHandler(@NonNull Looper looper) { + super(looper); + } + + @Override + public void handleMessage(@NonNull Message msg) { + switch (msg.what) { + //arg1 == enabled + case MODE_CHANGED: + handleAllCallbacks(msg.arg1 == 1); + break; + //arg1 == enabled + //obj == callback + case CALLBACK_ADDED: + handleSingleCallback(msg.arg1 == 1, (IAdvancedProtectionCallback) msg.obj); + break; + } + } + + private void handleAllCallbacks(boolean enabled) { + ArrayList<IAdvancedProtectionCallback> deadObjects = new ArrayList<>(); + + synchronized (mCallbacks) { + for (int i = 0; i < mCallbacks.size(); i++) { + IAdvancedProtectionCallback callback = mCallbacks.valueAt(i); + try { + callback.onAdvancedProtectionChanged(enabled); + } catch (RemoteException e) { + deadObjects.add(callback); + } + } + + for (int i = 0; i < deadObjects.size(); i++) { + mCallbacks.remove(deadObjects.get(i).asBinder()); + } + } + } + + private void handleSingleCallback(boolean enabled, IAdvancedProtectionCallback callback) { + try { + callback.onAdvancedProtectionChanged(enabled); + } catch (RemoteException e) { + mCallbacks.remove(callback.asBinder()); + } + } + } + + private final class DeathRecipient implements IBinder.DeathRecipient { + private final IBinder mBinder; + + DeathRecipient(IBinder binder) { + mBinder = binder; + } + + @Override + public void binderDied() { + synchronized (mCallbacks) { + mCallbacks.remove(mBinder); + } + } + } +} diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java new file mode 100644 index 000000000000..42505ad2de3f --- /dev/null +++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.advancedprotection; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.os.RemoteException; +import android.os.ShellCommand; + +import java.io.PrintWriter; + +class AdvancedProtectionShellCommand extends ShellCommand { + private AdvancedProtectionService mService; + + AdvancedProtectionShellCommand(@NonNull AdvancedProtectionService service) { + mService = service; + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + final PrintWriter pw = getOutPrintWriter(); + try { + switch (cmd) { + case "help": + onHelp(); + return 0; + case "set-protection-enabled": + return setProtectionEnabled(); + case "is-protection-enabled": + return isProtectionEnabled(pw); + } + } catch (RemoteException e) { + pw.println("Remote exception: " + e); + } + return -1; + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + dumpHelp(pw); + } + + private void dumpHelp(@NonNull PrintWriter pw) { + pw.println("Advanced Protection Mode commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" set-protection-enabled [true|false]"); + pw.println(" is-protection-enabled"); + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + private int setProtectionEnabled() throws RemoteException { + String protectionMode = getNextArgRequired(); + mService.setAdvancedProtectionEnabled(Boolean.parseBoolean(protectionMode)); + return 0; + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + private int isProtectionEnabled(@NonNull PrintWriter pw) throws RemoteException { + boolean protectionMode = mService.isAdvancedProtectionEnabled(); + pw.println(protectionMode); + return 0; + } +} diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java new file mode 100644 index 000000000000..07639d1a3945 --- /dev/null +++ b/services/core/java/com/android/server/security/forensic/ForensicService.java @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.forensic; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.security.forensic.IForensicService; +import android.security.forensic.IForensicServiceCommandCallback; +import android.security.forensic.IForensicServiceStateCallback; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.ServiceThread; +import com.android.server.SystemService; + +import java.util.ArrayList; + +/** + * @hide + */ +public class ForensicService extends SystemService { + private static final String TAG = "ForensicService"; + + private static final int MSG_MONITOR_STATE = 0; + private static final int MSG_MAKE_VISIBLE = 1; + private static final int MSG_MAKE_INVISIBLE = 2; + private static final int MSG_ENABLE = 3; + private static final int MSG_DISABLE = 4; + private static final int MSG_BACKUP = 5; + + private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN; + private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE; + private static final int STATE_VISIBLE = IForensicServiceStateCallback.State.VISIBLE; + private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED; + + private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN; + private static final int ERROR_PERMISSION_DENIED = + IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED; + private static final int ERROR_INVALID_STATE_TRANSITION = + IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION; + private static final int ERROR_BACKUP_TRANSPORT_UNAVAILABLE = + IForensicServiceCommandCallback.ErrorCode.BACKUP_TRANSPORT_UNAVAILABLE; + private static final int ERROR_DATA_SOURCE_UNAVAILABLE = + IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE; + + private final Context mContext; + private final Handler mHandler; + private final BinderService mBinderService; + + private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>(); + private volatile int mState = STATE_INVISIBLE; + + public ForensicService(@NonNull Context context) { + this(new InjectorImpl(context)); + } + + @VisibleForTesting + ForensicService(@NonNull Injector injector) { + super(injector.getContext()); + mContext = injector.getContext(); + mHandler = new EventHandler(injector.getLooper(), this); + mBinderService = new BinderService(this); + } + + @VisibleForTesting + protected void setState(int state) { + mState = state; + } + + private static final class BinderService extends IForensicService.Stub { + final ForensicService mService; + + BinderService(ForensicService service) { + mService = service; + } + + @Override + public void monitorState(IForensicServiceStateCallback callback) { + mService.mHandler.obtainMessage(MSG_MONITOR_STATE, callback).sendToTarget(); + } + + @Override + public void makeVisible(IForensicServiceCommandCallback callback) { + mService.mHandler.obtainMessage(MSG_MAKE_VISIBLE, callback).sendToTarget(); + } + + @Override + public void makeInvisible(IForensicServiceCommandCallback callback) { + mService.mHandler.obtainMessage(MSG_MAKE_INVISIBLE, callback).sendToTarget(); + } + + @Override + public void enable(IForensicServiceCommandCallback callback) { + mService.mHandler.obtainMessage(MSG_ENABLE, callback).sendToTarget(); + } + + @Override + public void disable(IForensicServiceCommandCallback callback) { + mService.mHandler.obtainMessage(MSG_DISABLE, callback).sendToTarget(); + } + } + + private static class EventHandler extends Handler { + private final ForensicService mService; + + EventHandler(Looper looper, ForensicService service) { + super(looper); + mService = service; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_MONITOR_STATE: + try { + mService.monitorState( + (IForensicServiceStateCallback) msg.obj); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + break; + case MSG_MAKE_VISIBLE: + try { + mService.makeVisible((IForensicServiceCommandCallback) msg.obj); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + break; + case MSG_MAKE_INVISIBLE: + try { + mService.makeInvisible((IForensicServiceCommandCallback) msg.obj); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + break; + case MSG_ENABLE: + try { + mService.enable((IForensicServiceCommandCallback) msg.obj); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + break; + case MSG_DISABLE: + try { + mService.disable((IForensicServiceCommandCallback) msg.obj); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + break; + default: + Slog.w(TAG, "Unknown message: " + msg.what); + } + } + } + + private void monitorState(IForensicServiceStateCallback callback) throws RemoteException { + for (int i = 0; i < mStateMonitors.size(); i++) { + if (mStateMonitors.get(i).asBinder() == callback.asBinder()) { + return; + } + } + mStateMonitors.add(callback); + callback.onStateChange(mState); + } + + private void notifyStateMonitors() throws RemoteException { + for (int i = 0; i < mStateMonitors.size(); i++) { + mStateMonitors.get(i).onStateChange(mState); + } + } + + private void makeVisible(IForensicServiceCommandCallback callback) throws RemoteException { + switch (mState) { + case STATE_INVISIBLE: + mState = STATE_VISIBLE; + notifyStateMonitors(); + callback.onSuccess(); + break; + case STATE_VISIBLE: + callback.onSuccess(); + break; + default: + callback.onFailure(ERROR_INVALID_STATE_TRANSITION); + } + } + + private void makeInvisible(IForensicServiceCommandCallback callback) throws RemoteException { + switch (mState) { + case STATE_VISIBLE: + case STATE_ENABLED: + mState = STATE_INVISIBLE; + notifyStateMonitors(); + callback.onSuccess(); + break; + case STATE_INVISIBLE: + callback.onSuccess(); + break; + default: + callback.onFailure(ERROR_INVALID_STATE_TRANSITION); + } + } + + private void enable(IForensicServiceCommandCallback callback) throws RemoteException { + switch (mState) { + case STATE_VISIBLE: + mState = STATE_ENABLED; + notifyStateMonitors(); + callback.onSuccess(); + break; + case STATE_ENABLED: + callback.onSuccess(); + break; + default: + callback.onFailure(ERROR_INVALID_STATE_TRANSITION); + } + } + + private void disable(IForensicServiceCommandCallback callback) throws RemoteException { + switch (mState) { + case STATE_ENABLED: + mState = STATE_VISIBLE; + notifyStateMonitors(); + callback.onSuccess(); + break; + case STATE_VISIBLE: + callback.onSuccess(); + break; + default: + callback.onFailure(ERROR_INVALID_STATE_TRANSITION); + } + } + + @Override + public void onStart() { + try { + publishBinderService(Context.FORENSIC_SERVICE, mBinderService); + } catch (Throwable t) { + Slog.e(TAG, "Could not start the ForensicService.", t); + } + } + + @VisibleForTesting + IForensicService getBinderService() { + return mBinderService; + } + + interface Injector { + Context getContext(); + + Looper getLooper(); + } + + private static final class InjectorImpl implements Injector { + private final Context mContext; + + InjectorImpl(Context context) { + mContext = context; + } + + @Override + public Context getContext() { + return mContext; + } + + + @Override + public Looper getLooper() { + ServiceThread serviceThread = + new ServiceThread( + TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */); + serviceThread.start(); + return serviceThread.getLooper(); + } + } +} + diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 91a17a9e1c31..4589d26261dc 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -381,10 +381,12 @@ public final class TvInputManagerService extends SystemService { // service to populate the hardware list. serviceState = new ServiceState(component, userId); userState.serviceStateMap.put(component, serviceState); - updateServiceConnectionLocked(component, userId); } else { inputList.addAll(serviceState.hardwareInputMap.values()); } + if (serviceState.needInit) { + updateServiceConnectionLocked(component, userId); + } } else { try { TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build(); @@ -489,6 +491,27 @@ public final class TvInputManagerService extends SystemService { } } + @GuardedBy("mLock") + private void cleanUpHdmiDevices(int userId) { + if (DEBUG) { + Slog.d(TAG, "cleanUpHdmiDevices: user " + userId); + } + UserState userState = getOrCreateUserStateLocked(userId); + for (ServiceState serviceState : userState.serviceStateMap.values()) { + for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) { + try { + if (serviceState.service != null) { + serviceState.service.notifyHdmiDeviceRemoved(device); + } else { + serviceState.hdmiDeviceRemovedBuffer.add(device); + } + } catch (RemoteException e) { + Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e); + } + } + } + } + private void startUser(int userId) { synchronized (mLock) { if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) { @@ -500,9 +523,13 @@ public final class TvInputManagerService extends SystemService { if (userInfo.isProfile() && parentInfo != null && parentInfo.id == mCurrentUserId) { - // only the children of the current user can be started in background + int prevUserId = mCurrentUserId; mCurrentUserId = userId; - startProfileLocked(userId); + // only the children of the current user can be started in background + releaseSessionOfUserLocked(prevUserId); + cleanUpHdmiDevices(prevUserId); + unbindServiceOfUserLocked(prevUserId); + startProfileLocked(mCurrentUserId); } } } @@ -515,6 +542,7 @@ public final class TvInputManagerService extends SystemService { } releaseSessionOfUserLocked(userId); + cleanUpHdmiDevices(userId); unbindServiceOfUserLocked(userId); mRunningProfiles.remove(userId); } @@ -543,15 +571,19 @@ public final class TvInputManagerService extends SystemService { unbindServiceOfUserLocked(runningId); } mRunningProfiles.clear(); - releaseSessionOfUserLocked(mCurrentUserId); - unbindServiceOfUserLocked(mCurrentUserId); + int prevUserId = mCurrentUserId; mCurrentUserId = userId; - buildTvInputListLocked(userId, null); - buildTvContentRatingSystemListLocked(userId); + + releaseSessionOfUserLocked(prevUserId); + cleanUpHdmiDevices(prevUserId); + unbindServiceOfUserLocked(prevUserId); + + buildTvInputListLocked(mCurrentUserId, null); + buildTvContentRatingSystemListLocked(mCurrentUserId); mMessageHandler .obtainMessage(MessageHandler.MSG_SWITCH_CONTENT_RESOLVER, - getContentResolverForUser(userId)) + getContentResolverForUser(mCurrentUserId)) .sendToTarget(); } } @@ -590,6 +622,9 @@ public final class TvInputManagerService extends SystemService { @GuardedBy("mLock") private void unbindServiceOfUserLocked(int userId) { + if (DEBUG) { + Slog.d(TAG, "unbindServiceOfUserLocked: user " + userId); + } UserState userState = getUserStateLocked(userId); if (userState == null) { return; @@ -600,7 +635,12 @@ public final class TvInputManagerService extends SystemService { ServiceState serviceState = userState.serviceStateMap.get(component); if (serviceState != null && serviceState.sessionTokens.isEmpty()) { unbindService(serviceState); - it.remove(); + if (!serviceState.isHardware) { + it.remove(); + } else { + serviceState.hardwareInputMap.clear(); + serviceState.needInit = true; + } } } } @@ -774,7 +814,7 @@ public final class TvInputManagerService extends SystemService { boolean shouldBind; if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) { shouldBind = !serviceState.sessionTokens.isEmpty() - || (serviceState.isHardware && serviceState.neverConnected); + || (serviceState.isHardware && serviceState.needInit); } else { // For a non-current user, // if sessionTokens is not empty, it contains recording sessions only @@ -3404,13 +3444,13 @@ public final class TvInputManagerService extends SystemService { private ServiceCallback callback; private boolean bound; private boolean reconnecting; - private boolean neverConnected; + private boolean needInit; private ServiceState(ComponentName component, int userId) { this.component = component; this.connection = new InputServiceConnection(component, userId); this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component); - this.neverConnected = true; + this.needInit = true; } } @@ -3618,11 +3658,9 @@ public final class TvInputManagerService extends SystemService { } ComponentName component = mTvInputHardwareManager.getInputMap().get(inputId).getComponent(); ServiceState serviceState = getServiceStateLocked(component, userId); - boolean removed = serviceState.hardwareInputMap.remove(inputId) != null; - if (removed) { - buildTvInputListLocked(userId, null); - mTvInputHardwareManager.removeHardwareInput(inputId); - } + serviceState.hardwareInputMap.remove(inputId); + buildTvInputListLocked(userId, null); + mTvInputHardwareManager.removeHardwareInput(inputId); } private final class InputServiceConnection implements ServiceConnection { @@ -3648,7 +3686,7 @@ public final class TvInputManagerService extends SystemService { } ServiceState serviceState = userState.serviceStateMap.get(mComponent); serviceState.service = ITvInputService.Stub.asInterface(service); - serviceState.neverConnected = false; + serviceState.needInit = false; // Register a callback, if we need to. if (serviceState.isHardware && serviceState.callback == null) { @@ -3841,9 +3879,12 @@ public final class TvInputManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { - Slog.d(TAG, "ServiceCallback: removeHardwareInput, inputId: " + inputId + - " by " + mComponent + ", userId: " + mUserId); - removeHardwareInputLocked(inputId, mUserId); + if (mUserId == mCurrentUserId) { + Slog.d(TAG, + "ServiceCallback: removeHardwareInput, inputId: " + inputId + " by " + + mComponent + ", userId: " + mUserId); + removeHardwareInputLocked(inputId, mUserId); + } } } finally { Binder.restoreCallingIdentity(identity); @@ -4578,6 +4619,11 @@ public final class TvInputManagerService extends SystemService { private final class HardwareListener implements TvInputHardwareManager.Listener { @Override public void onStateChanged(String inputId, int state) { + if (DEBUG) { + Slog.d(TAG, + "onStateChanged: inputId " + (inputId != null ? inputId : "null") + + ", state " + state); + } synchronized (mLock) { setStateLocked(inputId, state, mCurrentUserId); } @@ -4585,6 +4631,11 @@ public final class TvInputManagerService extends SystemService { @Override public void onHardwareDeviceAdded(TvInputHardwareInfo info) { + if (DEBUG) { + Slog.d(TAG, + "onHardwareDeviceAdded: TvInputHardwareInfo " + + (info != null ? info.toString() : "null")); + } synchronized (mLock) { UserState userState = getOrCreateUserStateLocked(mCurrentUserId); // Broadcast the event to all hardware inputs. @@ -4607,6 +4658,11 @@ public final class TvInputManagerService extends SystemService { @Override public void onHardwareDeviceRemoved(TvInputHardwareInfo info) { + if (DEBUG) { + Slog.d(TAG, + "onHardwareDeviceRemoved: TvInputHardwareInfo " + + (info != null ? info.toString() : "null")); + } synchronized (mLock) { String relatedInputId = mTvInputHardwareManager.getHardwareInputIdMap().get(info.getDeviceId()); @@ -4634,6 +4690,11 @@ public final class TvInputManagerService extends SystemService { @Override public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { + if (DEBUG) { + Slog.d(TAG, + "onHdmiDeviceAdded: HdmiDeviceInfo " + + (deviceInfo != null ? deviceInfo.toString() : "null")); + } synchronized (mLock) { UserState userState = getOrCreateUserStateLocked(mCurrentUserId); // Broadcast the event to all hardware inputs. @@ -4656,6 +4717,11 @@ public final class TvInputManagerService extends SystemService { @Override public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { + if (DEBUG) { + Slog.d(TAG, + "onHdmiDeviceRemoved: HdmiDeviceInfo " + + (deviceInfo != null ? deviceInfo.toString() : "null")); + } synchronized (mLock) { String relatedInputId = mTvInputHardwareManager.getHdmiInputIdMap().get(deviceInfo.getId()); @@ -4683,6 +4749,12 @@ public final class TvInputManagerService extends SystemService { @Override public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) { + if (DEBUG) { + Slog.d(TAG, + "onHdmiDeviceUpdated: inputId " + (inputId != null ? inputId : "null") + + ", deviceInfo: " + + (deviceInfo != null ? deviceInfo.toString() : "null")); + } synchronized (mLock) { Integer state; switch (deviceInfo.getDevicePowerStatus()) { diff --git a/services/core/java/com/android/server/uri/NeededUriGrants.java b/services/core/java/com/android/server/uri/NeededUriGrants.java index 8c8f55304fbb..2fe61e00c97e 100644 --- a/services/core/java/com/android/server/uri/NeededUriGrants.java +++ b/services/core/java/com/android/server/uri/NeededUriGrants.java @@ -17,10 +17,13 @@ package com.android.server.uri; import android.util.ArraySet; +import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.server.am.NeededUriGrantsProto; +import java.util.Objects; + /** List of {@link GrantUri} a process needs. */ public class NeededUriGrants { final String targetPkg; @@ -35,6 +38,20 @@ public class NeededUriGrants { this.uris = new ArraySet<>(); } + public void merge(NeededUriGrants other) { + if (other == null) return; + if (!Objects.equals(this.targetPkg, other.targetPkg) + || this.targetUid != other.targetUid || this.flags != other.flags) { + Slog.wtf("NeededUriGrants", + "The other NeededUriGrants does not share the same targetUid, targetPkg or " + + "flags. It cannot be merged into this NeededUriGrants. This " + + "NeededUriGrants: " + this.toStringWithoutUri() + + ". Other NeededUriGrants: " + other.toStringWithoutUri()); + } else { + this.uris.addAll(other.uris); + } + } + public void dumpDebug(ProtoOutputStream proto, long fieldId) { long token = proto.start(fieldId); proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg); @@ -47,4 +64,12 @@ public class NeededUriGrants { } proto.end(token); } + + public String toStringWithoutUri() { + return "NeededUriGrants{" + + "targetPkg='" + targetPkg + '\'' + + ", targetUid=" + targetUid + + ", flags=" + flags + + '}'; + } } diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java index fbcc856d0974..d192e64c897f 100644 --- a/services/core/java/com/android/server/vibrator/HalVibration.java +++ b/services/core/java/com/android/server/vibrator/HalVibration.java @@ -21,6 +21,8 @@ import android.annotation.Nullable; import android.os.CombinedVibration; import android.os.VibrationAttributes; import android.os.VibrationEffect; +import android.os.VibratorInfo; +import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.VibrationEffectSegment; import android.util.SparseArray; @@ -145,19 +147,30 @@ final class HalVibration extends Vibration { originalEffect, mScaleLevel, mAdaptiveScale); } - /** - * Returns true if this vibration can pipeline with the specified one. - * - * <p>Note that currently, repeating vibrations can't pipeline with following vibrations, - * because the cancel() call to stop the repetition will cancel a pending vibration too. This - * can be changed if we have a use-case to reason around behavior for. It may also be nice to - * pipeline very short vibrations together, regardless of the flag. - */ - public boolean canPipelineWith(HalVibration vib) { - return callerInfo.uid == vib.callerInfo.uid && callerInfo.attrs.isFlagSet( - VibrationAttributes.FLAG_PIPELINED_EFFECT) - && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT) - && (mOriginalEffect.getDuration() != Long.MAX_VALUE); + /** Returns true if this vibration can pipeline with the specified one. */ + public boolean canPipelineWith(HalVibration vib, + @Nullable SparseArray<VibratorInfo> vibratorInfos, int durationThresholdMs) { + long effectDuration = Flags.vibrationPipelineEnabled() && (vibratorInfos != null) + ? mEffectToPlay.getDuration(vibratorInfos) + : mEffectToPlay.getDuration(); + if (effectDuration == Long.MAX_VALUE) { + // Repeating vibrations can't pipeline with following vibrations, because the cancel() + // call to stop the repetition will cancel a pending vibration too. This can be changed + // if we have a use-case, requiring changes to how pipelined vibrations are cancelled. + return false; + } + if (Flags.vibrationPipelineEnabled() + && (effectDuration > 0) && (effectDuration < durationThresholdMs)) { + // Duration is known and it's less than the pipeline threshold, so allow it. + // No need to check UID, as we want to avoid cancelling any short effect and let the + // vibrator hardware gracefully finish the vibration. + return true; + } + // Check the same app is requesting multiple vibrations with the pipeline flag, + // independently of the effect durations. + return callerInfo.uid == vib.callerInfo.uid + && callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT) + && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT); } private void fillFallbacksForEffect(CombinedVibration effect, diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 9b7bdece69f9..7d5d34dbf7ab 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -168,12 +168,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @VisibleForTesting final VibrationSettings mVibrationSettings; + private final VibrationConfig mVibrationConfig; private final VibrationScaler mVibrationScaler; private final VibratorControlService mVibratorControlService; private final InputDeviceDelegate mInputDeviceDelegate; private final DeviceAdapter mDeviceAdapter; @GuardedBy("mLock") + @Nullable private SparseArray<VibratorInfo> mVibratorInfos; + @GuardedBy("mLock") @Nullable private VibratorInfo mCombinedVibratorInfo; @GuardedBy("mLock") @Nullable private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider; @@ -247,9 +250,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mHandler = injector.createHandler(Looper.myLooper()); mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler); - VibrationConfig vibrationConfig = new VibrationConfig(context.getResources()); - mVibrationSettings = new VibrationSettings(mContext, mHandler, vibrationConfig); - mVibrationScaler = new VibrationScaler(vibrationConfig, mVibrationSettings); + mVibrationConfig = new VibrationConfig(context.getResources()); + mVibrationSettings = new VibrationSettings(mContext, mHandler, mVibrationConfig); + mVibrationScaler = new VibrationScaler(mVibrationConfig, mVibrationSettings); mVibratorControlService = new VibratorControlService(mContext, injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings, mFrameworkStatsLogger, mLock); @@ -295,7 +298,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mVibratorIds = vibratorIds; mVibrators = new SparseArray<>(mVibratorIds.length); for (int vibratorId : vibratorIds) { - mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener)); + VibratorController vibratorController = + injector.createVibratorController(vibratorId, listener); + mVibrators.put(vibratorId, vibratorController); } } @@ -334,6 +339,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mVibrators.valueAt(i).reloadVibratorInfoIfNeeded(); } + synchronized (mLock) { + mVibratorInfos = transformAllVibratorsLocked(VibratorController::getVibratorInfo); + VibratorInfo[] infos = new VibratorInfo[mVibratorInfos.size()]; + for (int i = 0; i < mVibratorInfos.size(); i++) { + infos[i] = mVibratorInfos.valueAt(i); + } + mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos); + } + mVibrationSettings.onSystemReady(); mInputDeviceDelegate.onSystemReady(); @@ -633,7 +647,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { endExternalVibrateLocked(Status.CANCELLED_SUPERSEDED, callerInfo, /* continueExternalControl= */ false); } else if (mCurrentVibration != null) { - if (mCurrentVibration.getVibration().canPipelineWith(vib)) { + if (mCurrentVibration.getVibration().canPipelineWith(vib, mVibratorInfos, + mVibrationConfig.getVibrationPipelineMaxDurationMs())) { // Don't cancel the current vibration if it's pipeline-able. // Note that if there is a pending next vibration that can't be // pipelined, it will have already cancelled the current one, so we @@ -1871,33 +1886,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } + @Nullable private VibratorInfo getCombinedVibratorInfo() { synchronized (mLock) { - // Used a cached resolving vibrator if one exists. - if (mCombinedVibratorInfo != null) { - return mCombinedVibratorInfo; - } - - // Return an empty resolving vibrator if the service has no vibrator. - if (mVibratorIds.length == 0) { - return mCombinedVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO; - } - - // Combine the vibrator infos of all the service's vibrator to create a single resolving - // vibrator that is based on the combined info. - VibratorInfo[] infos = new VibratorInfo[mVibratorIds.length]; - for (int i = 0; i < mVibratorIds.length; i++) { - VibratorInfo info = getVibratorInfo(mVibratorIds[i]); - // If any one of the service's vibrator does not have a valid vibrator info, stop - // trying to create and cache a combined resolving vibrator. Combine the infos only - // when infos for all vibrators are available. - if (info == null) { - return null; - } - infos[i] = info; - } - - return mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos); + // This is only initialized at system ready, when all vibrator infos are fully loaded. + return mCombinedVibratorInfo; } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 2d75f35d2a9c..da9a67640f77 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -537,7 +537,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub * @return true unless the wallpaper changed during the color computation */ private boolean extractColors(WallpaperData wallpaper) { - if (offloadColorExtraction()) return !mImageWallpaper.equals(wallpaper.getComponent()); + if (offloadColorExtraction()) return true; String cropFile = null; boolean defaultImageWallpaper = false; int wallpaperId; diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index ae30fcde39e1..d119a08b0c85 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -442,8 +442,6 @@ class ActivityClientController extends IActivityClientController.Stub { throw new IllegalArgumentException("File descriptors passed in Intent"); } - mService.mAmInternal.addCreatorToken(resultData); - final ActivityRecord r; synchronized (mGlobalLock) { r = ActivityRecord.isInRootTaskLocked(token); @@ -502,6 +500,8 @@ class ActivityClientController extends IActivityClientController.Stub { r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis()); } + mService.mAmInternal.addCreatorToken(resultData, r.packageName); + final long origId = Binder.clearCallingIdentity(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity"); try { diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 0580d4a5a4a3..c1f5a27b81e7 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL; +import static com.android.server.wm.ActivityStarter.Request.DEFAULT_INTENT_CREATOR_UID; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; @@ -441,6 +442,17 @@ public class ActivityStartController { 0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid, callingPid); aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId); + int creatorUid = DEFAULT_INTENT_CREATOR_UID; + String creatorPackage = null; + if (ActivityManagerService.IntentCreatorToken.isValid(intent)) { + ActivityManagerService.IntentCreatorToken creatorToken = + (ActivityManagerService.IntentCreatorToken) intent.getCreatorToken(); + if (creatorToken.getCreatorUid() != filterCallingUid) { + creatorUid = creatorToken.getCreatorUid(); + creatorPackage = creatorToken.getCreatorPackage(); + } + // leave creatorUid as -1 if the intent creator is the same as the launcher + } if (aInfo != null) { try { @@ -454,6 +466,24 @@ public class ActivityStartController { return START_CANCELED; } + if (creatorUid != DEFAULT_INTENT_CREATOR_UID) { + try { + NeededUriGrants creatorIntentGrants = mSupervisor.mService.mUgmInternal + .checkGrantUriPermissionFromIntent(intent, creatorUid, + aInfo.applicationInfo.packageName, + UserHandle.getUserId(aInfo.applicationInfo.uid)); + if (intentGrants == null) { + intentGrants = creatorIntentGrants; + } else { + intentGrants.merge(creatorIntentGrants); + } + } catch (SecurityException securityException) { + ActivityStarter.logForIntentRedirect( + "Creator URI Grant Caused Exception.", intent, creatorUid, + creatorPackage, filterCallingUid, callingPackage); + // TODO b/368559093 - rethrow the securityException. + } + } if ((aInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) { throw new IllegalArgumentException( @@ -477,6 +507,8 @@ public class ActivityStartController { .setCallingUid(callingUid) .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) + .setIntentCreatorUid(creatorUid) + .setIntentCreatorPackage(creatorPackage) .setRealCallingPid(realCallingPid) .setRealCallingUid(realCallingUid) .setActivityOptions(checkedOptions) diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 87fa62ac0e3b..5d3ae54f0934 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -86,6 +86,7 @@ import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENS import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK; import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST; import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg; import android.annotation.IntDef; @@ -131,6 +132,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.app.IVoiceInteractor; import com.android.internal.protolog.ProtoLog; +import com.android.server.am.ActivityManagerService.IntentCreatorToken; import com.android.server.am.PendingIntentRecord; import com.android.server.pm.InstantAppResolver; import com.android.server.pm.PackageArchiver; @@ -383,6 +385,7 @@ class ActivityStarter { private static final int DEFAULT_CALLING_PID = 0; static final int DEFAULT_REAL_CALLING_UID = -1; static final int DEFAULT_REAL_CALLING_PID = 0; + static final int DEFAULT_INTENT_CREATOR_UID = -1; IApplicationThread caller; Intent intent; @@ -403,6 +406,8 @@ class ActivityStarter { @Nullable String callingFeatureId; int realCallingPid = DEFAULT_REAL_CALLING_PID; int realCallingUid = DEFAULT_REAL_CALLING_UID; + int intentCreatorUid = DEFAULT_INTENT_CREATOR_UID; + String intentCreatorPackage; int startFlags; SafeActivityOptions activityOptions; boolean ignoreTargetSecurity; @@ -463,6 +468,8 @@ class ActivityStarter { callingPid = DEFAULT_CALLING_PID; callingUid = DEFAULT_CALLING_UID; callingPackage = null; + intentCreatorUid = DEFAULT_INTENT_CREATOR_UID; + intentCreatorPackage = null; callingFeatureId = null; realCallingPid = DEFAULT_REAL_CALLING_PID; realCallingUid = DEFAULT_REAL_CALLING_UID; @@ -555,12 +562,14 @@ class ActivityStarter { // "resolved" calling UID, where we try our best to identify the // actual caller that is starting this activity int resolvedCallingUid = callingUid; + String resolvedCallingPackage = callingPackage; if (caller != null) { synchronized (supervisor.mService.mGlobalLock) { final WindowProcessController callerApp = supervisor.mService .getProcessController(caller); if (callerApp != null) { resolvedCallingUid = callerApp.mInfo.uid; + resolvedCallingPackage = callerApp.mInfo.packageName; } } } @@ -596,7 +605,23 @@ class ActivityStarter { // Collect information about the target of the Intent. activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags, profilerInfo); - + // Check if the Intent was redirected + if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN) + != 0) { + ActivityStarter.logForIntentRedirect( + "Unparceled intent does not have a creator token set.", intent, + intentCreatorUid, + intentCreatorPackage, resolvedCallingUid, resolvedCallingPackage); + // TODO b/368559093 - eventually ramp up to throw SecurityException + } + if (IntentCreatorToken.isValid(intent)) { + IntentCreatorToken creatorToken = (IntentCreatorToken) intent.getCreatorToken(); + if (creatorToken.getCreatorUid() != resolvedCallingUid) { + intentCreatorUid = creatorToken.getCreatorUid(); + intentCreatorPackage = creatorToken.getCreatorPackage(); + } + // leave intentCreatorUid as -1 if the intent creator is the same as the launcher + } // Carefully collect grants without holding lock if (activityInfo != null) { if (android.security.Flags.contentUriPermissionApis()) { @@ -606,11 +631,52 @@ class ActivityStarter { UserHandle.getUserId(activityInfo.applicationInfo.uid), activityInfo.requireContentUriPermissionFromCaller, /* requestHashCode */ this.hashCode()); + if (intentCreatorUid != DEFAULT_INTENT_CREATOR_UID) { + try { + NeededUriGrants creatorIntentGrants = supervisor.mService.mUgmInternal + .checkGrantUriPermissionFromIntent(intent, intentCreatorUid, + activityInfo.applicationInfo.packageName, + UserHandle.getUserId(activityInfo.applicationInfo.uid), + activityInfo.requireContentUriPermissionFromCaller, + /* requestHashCode */ this.hashCode()); + if (intentGrants == null) { + intentGrants = creatorIntentGrants; + } else { + intentGrants.merge(creatorIntentGrants); + } + } catch (SecurityException securityException) { + ActivityStarter.logForIntentRedirect( + "Creator URI Grant Caused Exception.", intent, intentCreatorUid, + intentCreatorPackage, resolvedCallingUid, + resolvedCallingPackage); + // TODO b/368559093 - rethrow the securityException. + } + } } else { intentGrants = supervisor.mService.mUgmInternal .checkGrantUriPermissionFromIntent(intent, resolvedCallingUid, activityInfo.applicationInfo.packageName, UserHandle.getUserId(activityInfo.applicationInfo.uid)); + if (intentCreatorUid != DEFAULT_INTENT_CREATOR_UID && intentGrants != null) { + try { + NeededUriGrants creatorIntentGrants = supervisor.mService.mUgmInternal + .checkGrantUriPermissionFromIntent(intent, intentCreatorUid, + activityInfo.applicationInfo.packageName, + UserHandle.getUserId( + activityInfo.applicationInfo.uid)); + if (intentGrants == null) { + intentGrants = creatorIntentGrants; + } else { + intentGrants.merge(creatorIntentGrants); + } + } catch (SecurityException securityException) { + ActivityStarter.logForIntentRedirect( + "Creator URI Grant Caused Exception.", intent, intentCreatorUid, + intentCreatorPackage, resolvedCallingUid, + resolvedCallingPackage); + // TODO b/368559093 - rethrow the securityException. + } + } } } } @@ -977,7 +1043,9 @@ class ActivityStarter { int requestCode = request.requestCode; int callingPid = request.callingPid; int callingUid = request.callingUid; - String callingPackage = request.callingPackage; + int intentCreatorUid = request.intentCreatorUid; + String intentCreatorPackage = request.intentCreatorPackage; + String intentCallingPackage = request.callingPackage; String callingFeatureId = request.callingFeatureId; final int realCallingPid = request.realCallingPid; final int realCallingUid = request.realCallingUid; @@ -1062,7 +1130,7 @@ class ActivityStarter { // launched in the app flow to redirect to an activity picked by the user, where // we want the final activity to consider it to have been launched by the // previous app activity. - callingPackage = sourceRecord.launchedFromPackage; + intentCallingPackage = sourceRecord.launchedFromPackage; callingFeatureId = sourceRecord.launchedFromFeatureId; } } @@ -1084,7 +1152,7 @@ class ActivityStarter { if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) { err = packageArchiver .requestUnarchiveOnActivityStart( - intent, callingPackage, mRequest.userId, realCallingUid); + intent, intentCallingPackage, mRequest.userId, realCallingUid); } } } @@ -1143,7 +1211,7 @@ class ActivityStarter { boolean abort; try { abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, - requestCode, callingPid, callingUid, callingPackage, callingFeatureId, + requestCode, callingPid, callingUid, intentCallingPackage, callingFeatureId, request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord, resultRootTask); } catch (SecurityException e) { @@ -1171,7 +1239,47 @@ class ActivityStarter { abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid, - callingPackage); + intentCallingPackage); + + if (intentCreatorUid != Request.DEFAULT_INTENT_CREATOR_UID) { + try { + if (!mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, + requestCode, 0, intentCreatorUid, intentCreatorPackage, "", + request.ignoreTargetSecurity, inTask != null, null, resultRecord, + resultRootTask)) { + logForIntentRedirect("Creator checkStartAnyActivityPermission Caused abortion.", + intent, intentCreatorUid, intentCreatorPackage, callingUid, + intentCallingPackage); + // TODO b/368559093 - set abort to true. + // abort = true; + } + } catch (SecurityException e) { + logForIntentRedirect("Creator checkStartAnyActivityPermission Caused Exception.", + intent, intentCreatorUid, intentCreatorPackage, callingUid, + intentCallingPackage); + // TODO b/368559093 - rethrow the exception. + //throw e; + } + if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid, 0, + resolvedType, aInfo.applicationInfo)) { + logForIntentRedirect("Creator IntentFirewall.checkStartActivity Caused abortion.", + intent, intentCreatorUid, intentCreatorPackage, callingUid, + intentCallingPackage); + // TODO b/368559093 - set abort to true. + // abort = true; + } + + if (!mService.getPermissionPolicyInternal().checkStartActivity(intent, intentCreatorUid, + intentCreatorPackage)) { + logForIntentRedirect( + "Creator PermissionPolicyService.checkStartActivity Caused abortion.", + intent, intentCreatorUid, intentCreatorPackage, callingUid, + intentCallingPackage); + // TODO b/368559093 - set abort to true. + // abort = true; + } + intent.removeCreatorTokenInfo(); + } // Merge the two options bundles, while realCallerOptions takes precedence. ActivityOptions checkedOptions = options != null @@ -1188,7 +1296,7 @@ class ActivityStarter { balController.checkBackgroundActivityStart( callingUid, callingPid, - callingPackage, + intentCallingPackage, realCallingUid, realCallingPid, callerApp, @@ -1209,7 +1317,7 @@ class ActivityStarter { if (request.allowPendingRemoteAnimationRegistryLookup) { checkedOptions = mService.getActivityStartController() .getPendingRemoteAnimationRegistry() - .overrideOptionsIfNeeded(callingPackage, checkedOptions); + .overrideOptionsIfNeeded(intentCallingPackage, checkedOptions); } if (mService.mController != null) { try { @@ -1225,7 +1333,8 @@ class ActivityStarter { final TaskDisplayArea suggestedLaunchDisplayArea = computeSuggestedLaunchDisplayArea(inTask, sourceRecord, checkedOptions); - mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage, + mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, + intentCallingPackage, callingFeatureId); if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment, callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) { @@ -1263,7 +1372,8 @@ class ActivityStarter { if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( aInfo.packageName, userId)) { final IIntentSender target = mService.getIntentSenderLocked( - ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingFeatureId, + ActivityManager.INTENT_SENDER_ACTIVITY, intentCallingPackage, + callingFeatureId, callingUid, userId, null, null, 0, new Intent[]{intent}, new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT, null); @@ -1326,7 +1436,8 @@ class ActivityStarter { // app [on install success]. if (rInfo != null && rInfo.auxiliaryInfo != null) { intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent, - callingPackage, callingFeatureId, verificationBundle, resolvedType, userId); + intentCallingPackage, callingFeatureId, verificationBundle, resolvedType, + userId); resolvedType = null; callingUid = realCallingUid; callingPid = realCallingPid; @@ -1349,7 +1460,7 @@ class ActivityStarter { .setCaller(callerApp) .setLaunchedFromPid(callingPid) .setLaunchedFromUid(callingUid) - .setLaunchedFromPackage(callingPackage) + .setLaunchedFromPackage(intentCallingPackage) .setLaunchedFromFeature(callingFeatureId) .setIntent(intent) .setResolvedType(resolvedType) @@ -2786,10 +2897,22 @@ class ActivityStarter { } } - if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0 - && ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 || mSourceRecord == null)) { - // ignore the flag if there is no the sourceRecord or without new_task flag - mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT; + if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { + final boolean hasNewTaskFlag = (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0; + if (!hasNewTaskFlag || mSourceRecord == null) { + // ignore the flag if there is no the sourceRecord or without new_task flag + Slog.w(TAG_WM, !hasNewTaskFlag + ? "Launch adjacent ignored due to missing NEW_TASK" + : "Launch adjacent ignored due to missing source activity"); + mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT; + } + // Ensure that the source task or its parents has not disabled launch-adjacent + if (mSourceRecord != null && mSourceRecord.getTask() != null && + mSourceRecord.getTask().isLaunchAdjacentDisabled()) { + Slog.w(TAG_WM, "Launch adjacent blocked by source task or ancestor"); + mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT; + } + } } @@ -3295,6 +3418,16 @@ class ActivityStarter { return this; } + ActivityStarter setIntentCreatorUid(int uid) { + mRequest.intentCreatorUid = uid; + return this; + } + + ActivityStarter setIntentCreatorPackage(String intentCreatorPackage) { + mRequest.intentCreatorPackage = intentCreatorPackage; + return this; + } + /** * Sets the pid of the caller who requested to launch the activity. * @@ -3454,4 +3587,19 @@ class ActivityStarter { pw.print(" mInTaskFragment="); pw.println(mInTaskFragment); } + + static void logForIntentRedirect(String message, Intent intent, int intentCreatorUid, + String intentCreatorPackage, int callingUid, String callingPackage) { + String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid, + intentCreatorPackage, callingUid, callingPackage); + Slog.wtf(TAG, msg); + } + + private static String getIntentRedirectPreventedLogMessage(String message, Intent intent, + int intentCreatorUid, String intentCreatorPackage, int callingUid, + String callingPackage) { + return "[IntentRedirect]" + message + " intentCreatorUid: " + intentCreatorUid + + "; intentCreatorPackage: " + intentCreatorPackage + "; callingUid: " + callingUid + + "; callingPackage: " + callingPackage + "; intent: " + intent; + } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 4db478a13c92..111e74e9b590 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1228,7 +1228,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) { - mAmInternal.addCreatorToken(intent); + mAmInternal.addCreatorToken(intent, callingPackage); return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, UserHandle.getCallingUserId()); @@ -1243,7 +1243,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { enforceNotIsolatedCaller(reason); if (intents != null) { for (Intent intent : intents) { - mAmInternal.addCreatorToken(intent); + mAmInternal.addCreatorToken(intent, callingPackage); } } userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason); @@ -1275,7 +1275,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) { - mAmInternal.addCreatorToken(intent); + mAmInternal.addCreatorToken(intent, callingPackage); final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions); assertPackageMatchesCallingUid(callingPackage); @@ -1330,7 +1330,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } // Remove existing mismatch flag so it can be properly updated later fillInIntent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); - mAmInternal.addCreatorToken(fillInIntent); } if (!(target instanceof PendingIntentRecord)) { @@ -1339,6 +1338,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { PendingIntentRecord pir = (PendingIntentRecord) target; + if (fillInIntent != null) { + mAmInternal.addCreatorToken(fillInIntent, pir.getPackageName()); + } + synchronized (mGlobalLock) { // If this is coming from the currently resumed activity, it is // effectively saying that app switches are allowed at this point. @@ -1349,6 +1352,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mAppSwitchesState = APP_SWITCH_ALLOW; } } + return pir.sendInner(caller, 0, fillInIntent, resolvedType, allowlistToken, null, null, resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions); } @@ -1361,8 +1365,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("File descriptors passed in Intent"); } - mAmInternal.addCreatorToken(intent); - SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions); synchronized (mGlobalLock) { @@ -1376,6 +1378,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { SafeActivityOptions.abort(options); return false; } + + mAmInternal.addCreatorToken(intent, r.packageName); + intent = new Intent(intent); // Remove existing mismatch flag so it can be properly updated later intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); @@ -6747,6 +6752,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { break; } } + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s", + wpc.mName, wpc.getConfiguration()); // The "info" can be the target of instrumentation. return new PreBindInfo(compatibilityInfoForPackageLocked(info), new Configuration(wpc.getConfiguration())); diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 4f71719006f5..023ff6e8455c 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2280,6 +2280,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { * sent to the new top resumed activity. */ ActivityRecord updateTopResumedActivityIfNeeded(String reason) { + if (!readyToResume()) { + return mTopResumedActivity; + } final ActivityRecord prevTopActivity = mTopResumedActivity; final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) { diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 4d17ed24e734..eee4c86bc483 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -160,7 +160,7 @@ class AppTaskImpl extends IAppTask.Stub { Intent intent, String resolvedType, Bundle bOptions) { checkCallerOrSystemOrRoot(); mService.assertPackageMatchesCallingUid(callingPackage); - mService.mAmInternal.addCreatorToken(intent); + mService.mAmInternal.addCreatorToken(intent, callingPackage); int callingUser = UserHandle.getCallingUserId(); Task task; diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 94cd2e64b057..70f9ebb0e61e 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -34,6 +34,7 @@ import static com.android.server.wm.BackNavigationProto.LAST_BACK_TYPE; import static com.android.server.wm.BackNavigationProto.MAIN_OPEN_ACTIVITY; import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK; +import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1237,8 +1238,9 @@ class BackNavigationController { } allWindowDrawn &= next.mAppWindowDrawn; } - // Do not remove until transition ready. - if (!activity.isVisible()) { + // Do not remove windowless surfaces if the transaction has not been applied. + if (activity.getSyncTransactionCommitCallbackDepth() > 0 + || activity.mSyncState != SYNC_STATE_NONE) { return; } if (allWindowDrawn) { diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index a380ba1a6f11..f9902cf0db9c 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -66,6 +66,7 @@ import android.app.BackgroundStartPrivileges; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; +import android.compat.annotation.Overridable; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -129,6 +130,7 @@ public class BackgroundActivityStartController { /** If enabled the creator will not allow BAL on its behalf by default. */ @ChangeId @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE) + @Overridable private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR = 296478951; public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED = diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 6a23aaa663ef..e9c6e93891df 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -274,34 +274,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { if (caller != controlTarget) { if (Flags.refactorInsetsController()) { if (isImeInputTarget(caller)) { - // In case of the multi window mode, update the requestedVisibleTypes from - // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController. - // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with - // its new requested visibility for the IME - boolean imeVisible = caller.isRequestedVisible(WindowInsets.Type.ime()); - if (controlTarget != null) { - ImeTracker.forLogging().onProgress(statsToken, - ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); - controlTarget.setImeInputTargetRequestedVisibility(imeVisible); - } else if (caller instanceof InsetsControlTarget) { - // In case of a virtual display that cannot show the IME, the - // controlTarget will be null here, as no controlTarget was set yet. In - // that case, proceed similar to the multi window mode (fallback = - // RemoteInsetsControlTarget of the default display) - controlTarget = mDisplayContent.getImeHostOrFallback( - ((InsetsControlTarget) caller).getWindow()); - - if (controlTarget != caller) { - ImeTracker.forLogging().onProgress(statsToken, - ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); - controlTarget.setImeInputTargetRequestedVisibility(imeVisible); - } else { - ImeTracker.forLogging().onFailed(statsToken, - ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); - } - } - - invokeOnImeRequestedChangedListener(caller, statsToken); + reportImeInputTargetStateToControlTarget(caller, controlTarget, statsToken); } else { // TODO(b/353463205) add ImeTracker? } @@ -332,14 +305,42 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { if (Flags.refactorInsetsController() && target != null) { InsetsControlTarget imeControlTarget = getControlTarget(); if (target != imeControlTarget) { - // If the targetWin is not the imeControlTarget (=RemoteInsetsControlTarget) let it - // know about the new requestedVisibleTypes for the IME. - if (imeControlTarget != null) { - imeControlTarget.setImeInputTargetRequestedVisibility( - (target.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0); - } + // TODO(b/353463205): start new request here? + reportImeInputTargetStateToControlTarget(target, imeControlTarget, + null /* statsToken */); + } + } + } + + private void reportImeInputTargetStateToControlTarget(@NonNull InsetsTarget imeInsetsTarget, + InsetsControlTarget controlTarget, @Nullable ImeTracker.Token statsToken) { + // In case of the multi window mode, update the requestedVisibleTypes from + // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController. + // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with + // its new requested visibility for the IME + boolean imeVisible = imeInsetsTarget.isRequestedVisible(WindowInsets.Type.ime()); + if (controlTarget != null) { + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); + controlTarget.setImeInputTargetRequestedVisibility(imeVisible); + } else if (imeInsetsTarget instanceof InsetsControlTarget) { + // In case of a virtual display that cannot show the IME, the + // controlTarget will be null here, as no controlTarget was set yet. In + // that case, proceed similar to the multi window mode (fallback = + // RemoteInsetsControlTarget of the default display) + controlTarget = mDisplayContent.getImeHostOrFallback( + ((InsetsControlTarget) imeInsetsTarget).getWindow()); + + if (controlTarget != imeInsetsTarget) { + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); + controlTarget.setImeInputTargetRequestedVisibility(imeVisible); + } else { + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); } } + invokeOnImeRequestedChangedListener(imeInsetsTarget, statsToken); } // TODO(b/353463205) check callers to see if we can make statsToken @NonNull diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index a4fe0647ea79..9d21183c6c03 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -131,7 +131,9 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal final boolean changed = !com.android.window.flags.Flags.filterIrrelevantInputDeviceChange() || updateLastInputConfigurationSources(); - if (changed) { + // Even if the input devices are not changed, there could be other pending changes + // during booting. It's fine to apply earlier. + if (changed || !mService.mDisplayEnabled) { synchronized (mService.mGlobalLock) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "inputDeviceConfigChanged"); mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration); diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 6067a9972bbe..1d4d6eb82c44 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -409,7 +409,7 @@ class InsetsSourceProvider { } final Point position = getWindowFrameSurfacePosition(); if (!mPosition.equals(position)) { - mPosition.set(position.x, position.y); + mPosition.set(position); if (windowState != null && windowState.getWindowFrames().didFrameSizeChange() && windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) { mHasPendingPosition = true; @@ -553,6 +553,7 @@ class InsetsSourceProvider { } boolean initiallyVisible = mClientVisible; final Point surfacePosition = getWindowFrameSurfacePosition(); + mPosition.set(surfacePosition); mAdapter = new ControlAdapter(surfacePosition); if (mSource.getType() == WindowInsets.Type.ime()) { if (android.view.inputmethod.Flags.refactorInsetsController()) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 00704b3525c5..4861341f830a 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -503,6 +503,11 @@ class Task extends TaskFragment { boolean mIsTrimmableFromRecents; /** + * Sets whether the launch-adjacent flag is respected or not for this task or its child tasks. + */ + private boolean mLaunchAdjacentDisabled; + + /** * Bounds offset should be applied when calculating compatible configuration for apps targeting * SDK level 34 or before. */ @@ -3802,6 +3807,9 @@ class Task extends TaskFragment { pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime); pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)"); pw.print(prefix); pw.println(" isTrimmable=" + mIsTrimmableFromRecents); + if (mLaunchAdjacentDisabled) { + pw.println(prefix + "mLaunchAdjacentDisabled=true"); + } } @Override @@ -5734,9 +5742,9 @@ class Task extends TaskFragment { } private boolean canMoveTaskToBack(Task task) { - // Checks whether a task is a child of this task because it can be reparetned when + // Checks whether a task is a child of this task because it can be reparented when // transition is deferred. - if (task != this && task.getParent() != this) { + if (task != this && !task.isDescendantOf(this)) { return false; } @@ -6274,6 +6282,28 @@ class Task extends TaskFragment { } /** + * Sets this task and its children to disable respecting launch-adjacent. + */ + void setLaunchAdjacentDisabled(boolean disabled) { + mLaunchAdjacentDisabled = disabled; + } + + /** + * Returns whether this task or any of its ancestors have disabled respecting the + * launch-adjacent flag. + */ + boolean isLaunchAdjacentDisabled() { + Task t = this; + while (t != null) { + if (t.mLaunchAdjacentDisabled) { + return true; + } + t = t.getParent().asTask(); + } + return false; + } + + /** * Return true if the activityInfo has the same requiredDisplayCategory as this task. */ boolean isSameRequiredDisplayCategory(@NonNull ActivityInfo info) { diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 5dd3bbce4e96..2c71c1a1f4f3 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1074,6 +1074,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { final Task launchRootTask = Task.fromWindowContainerToken(options.getLaunchRootTask()); // We only allow this for created by organizer tasks. if (launchRootTask != null && launchRootTask.mCreatedByOrganizer) { + Slog.i(TAG_WM, "Using launch root task from activity options: taskId=" + + launchRootTask.mTaskId); return launchRootTask; } } @@ -1081,19 +1083,25 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // Use launch-adjacent-flag-root if launching with launch-adjacent flag. if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0 && mLaunchAdjacentFlagRootTask != null) { + final Task launchAdjacentRootAdjacentTask = + mLaunchAdjacentFlagRootTask.getAdjacentTask(); if (sourceTask != null && (sourceTask == candidateTask || sourceTask.topRunningActivity() == null)) { // Do nothing when task that is getting opened is same as the source or when // the source is no-longer valid. Slog.w(TAG_WM, "Ignoring LAUNCH_ADJACENT because adjacent source is gone."); } else if (sourceTask != null - && mLaunchAdjacentFlagRootTask.getAdjacentTask() != null + && launchAdjacentRootAdjacentTask != null && (sourceTask == mLaunchAdjacentFlagRootTask || sourceTask.isDescendantOf(mLaunchAdjacentFlagRootTask))) { - // If the adjacent launch is coming from the same root, launch to - // adjacent root instead. - return mLaunchAdjacentFlagRootTask.getAdjacentTask(); + // If the adjacent launch is coming from the same root that was specified as the + // launch-adjacent task, so instead we launch to its adjacent root instead. + Slog.i(TAG_WM, "Using adjacent-to specified launch-adjacent task: taskId=" + + launchAdjacentRootAdjacentTask.mTaskId); + return launchAdjacentRootAdjacentTask; } else { + Slog.i(TAG_WM, "Using specified launch-adjacent task: taskId=" + + mLaunchAdjacentFlagRootTask.mTaskId); return mLaunchAdjacentFlagRootTask; } } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 82c7a9350eca..166d74b132bd 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -69,6 +69,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; @@ -1450,6 +1451,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub task.setTrimmableFromRecents(hop.isTrimmableFromRecents()); break; } + case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT: { + final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); + final Task task = container != null ? container.asTask() : null; + if (task == null || !task.isAttached()) { + Slog.e(TAG, "Attempt to operate on unknown or detached container: " + + container); + break; + } + task.setLaunchAdjacentDisabled(hop.isLaunchAdjacentDisabled()); + break; + } case HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION: { if (mService.mBackNavigationController.restoreBackNavigation()) { effects |= TRANSACT_EFFECTS_LIFECYCLE; diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 86adc1944371..1a107c24a16a 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -133,7 +133,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private int mRapidActivityLaunchCount; // all about the first app in the process - final ApplicationInfo mInfo; + volatile ApplicationInfo mInfo; final String mName; final int mUid; @@ -1805,12 +1805,17 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio Configuration overrideConfig = new Configuration(r.getRequestedOverrideConfiguration()); overrideConfig.assetsSeq = assetSeq; r.onRequestedOverrideConfigurationChanged(overrideConfig); + r.updateApplicationInfo(mInfo); if (r.isVisibleRequested()) { r.ensureActivityConfiguration(); } } } + public void updateApplicationInfo(ApplicationInfo aInfo) { + mInfo = aInfo; + } + /** * This is called for sending {@link android.app.servertransaction.LaunchActivityItem}. * The caller must call {@link #setLastReportedConfiguration} if the delivered configuration diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b9727f9f3970..0b7ce75136bb 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -246,6 +246,7 @@ import com.android.server.security.AttestationVerificationManagerService; import com.android.server.security.FileIntegrityService; import com.android.server.security.KeyAttestationApplicationIdProviderService; import com.android.server.security.KeyChainSystemService; +import com.android.server.security.advancedprotection.AdvancedProtectionService; import com.android.server.security.rkp.RemoteProvisioningService; import com.android.server.selinux.SelinuxAuditLogsService; import com.android.server.sensorprivacy.SensorPrivacyService; @@ -327,8 +328,6 @@ public final class SystemServer implements Dumpable { * Implementation class names for services in the {@code SYSTEMSERVERCLASSPATH} * from {@code PRODUCT_SYSTEM_SERVER_JARS} that are *not* in {@code services.jar}. */ - private static final String ARC_NETWORK_SERVICE_CLASS = - "com.android.server.arc.net.ArcNetworkService"; private static final String ARC_PERSISTENT_DATA_BLOCK_SERVICE_CLASS = "com.android.server.arc.persistent_data_block.ArcPersistentDataBlockService"; private static final String ARC_SYSTEM_HEALTH_SERVICE = @@ -1760,6 +1759,12 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(AppFunctionManagerService.class); t.traceEnd(); } + + if (android.security.Flags.aapmApi()) { + t.traceBegin("StartAdvancedProtectionService"); + mSystemServiceManager.startService(AdvancedProtectionService.Lifecycle.class); + t.traceEnd(); + } } catch (Throwable e) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting core service"); @@ -2101,24 +2106,13 @@ public final class SystemServer implements Dumpable { if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI)) { // Wifi Service must be started first for wifi-related services. - if (!isArc) { - t.traceBegin("StartWifi"); - mSystemServiceManager.startServiceFromJar( - WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); - t.traceEnd(); - t.traceBegin("StartWifiScanning"); - mSystemServiceManager.startServiceFromJar( - WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); - t.traceEnd(); - } - } - - // ARC - ArcNetworkService registers the ARC network stack and replaces the - // stock WiFi service in both ARC++ container and ARCVM. Always starts the ARC network - // stack regardless of whether FEATURE_WIFI is enabled/disabled (b/254755875). - if (isArc) { - t.traceBegin("StartArcNetworking"); - mSystemServiceManager.startService(ARC_NETWORK_SERVICE_CLASS); + t.traceBegin("StartWifi"); + mSystemServiceManager.startServiceFromJar( + WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + t.traceBegin("StartWifiScanning"); + mSystemServiceManager.startServiceFromJar( + WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); t.traceEnd(); } diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index cc340c0a5f79..891c3349a43f 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -58,6 +58,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.EventLog; import android.util.Log; +import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; @@ -1737,6 +1738,11 @@ public class MidiService extends IMidiManager.Stub { pw.decreaseIndent(); } + @Override + protected void onUnhandledException(int code, int flags, Exception e) { + Slog.wtf(TAG, "Uncaught exception in AudioService: " + code + ", " + flags, e); + } + @GuardedBy("mUsbMidiLock") private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) { String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME); diff --git a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java index ff901af3defa..30df4c821134 100644 --- a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java +++ b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java @@ -96,6 +96,8 @@ class CallLogQueryHelper { } catch (SecurityException ex) { Slog.e(TAG, "Query call log failed: " + ex); return false; + } catch (Exception e) { + Slog.e(TAG, "Exception when querying call log.", e); } return hasResults; } diff --git a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java index 2505abf2d160..2bd9d87b0124 100644 --- a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java +++ b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java @@ -151,9 +151,11 @@ class ContactsQueryHelper { found = true; } } catch (SQLiteException exception) { - Slog.w("SQLite exception when querying contacts.", exception); + Slog.w(TAG, "SQLite exception when querying contacts.", exception); } catch (IllegalArgumentException exception) { - Slog.w("Illegal Argument exception when querying contacts.", exception); + Slog.w(TAG, "Illegal Argument exception when querying contacts.", exception); + } catch (Exception exception) { + Slog.e(TAG, "Exception when querying contacts.", exception); } if (found && lookupKey != null && hasPhoneNumber) { return queryPhoneNumber(lookupKey); @@ -181,6 +183,8 @@ class ContactsQueryHelper { mPhoneNumber = cursor.getString(phoneNumIdx); } } + } catch (Exception exception) { + Slog.e(TAG, "Exception when querying contact phone number.", exception); } return true; } diff --git a/services/people/java/com/android/server/people/data/MmsQueryHelper.java b/services/people/java/com/android/server/people/data/MmsQueryHelper.java index 39dba9c73ba2..414a523fb186 100644 --- a/services/people/java/com/android/server/people/data/MmsQueryHelper.java +++ b/services/people/java/com/android/server/people/data/MmsQueryHelper.java @@ -100,6 +100,8 @@ class MmsQueryHelper { } } } + } catch (Exception e) { + Slog.e(TAG, "Exception when querying MMS table.", e); } finally { Binder.defaultBlockingForCurrentThread(); } @@ -133,6 +135,8 @@ class MmsQueryHelper { address = cursor.getString(addrIndex); } } + } catch (Exception e) { + Slog.e(TAG, "Exception when querying MMS address table.", e); } if (!Mms.isPhoneNumber(address)) { return null; diff --git a/services/people/java/com/android/server/people/data/SmsQueryHelper.java b/services/people/java/com/android/server/people/data/SmsQueryHelper.java index a5eb3a581616..f8ff3abc8e4c 100644 --- a/services/people/java/com/android/server/people/data/SmsQueryHelper.java +++ b/services/people/java/com/android/server/people/data/SmsQueryHelper.java @@ -98,6 +98,8 @@ class SmsQueryHelper { } } } + } catch (Exception e) { + Slog.e(TAG, "Exception when querying SMS table.", e); } finally { Binder.defaultBlockingForCurrentThread(); } diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt index cbca434a6bb6..1e8935960a3d 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt @@ -21,6 +21,7 @@ import android.content.pm.PackageInstaller.SessionParams import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DEFAULT import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DENIED import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED +import android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED import android.content.pm.PackageManager import android.content.pm.verify.domain.DomainSet import android.os.Parcel @@ -33,6 +34,11 @@ import com.android.internal.os.BackgroundThread import com.android.server.pm.verify.pkg.VerifierController import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat +import java.io.File +import java.io.FileInputStream +import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.IOException import libcore.io.IoUtils import org.junit.Before import org.junit.Rule @@ -46,11 +52,6 @@ import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException -import java.io.File -import java.io.FileInputStream -import java.io.FileNotFoundException -import java.io.FileOutputStream -import java.io.IOException @Presubmit class PackageInstallerSessionTest { @@ -197,7 +198,8 @@ class PackageInstallerSessionTest { /* stagedSessionErrorCode */ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, /* stagedSessionErrorMessage */ "some error", /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")), - /* VerifierController */ mock(VerifierController::class.java) + /* VerifierController */ mock(VerifierController::class.java), + VERIFICATION_POLICY_BLOCK_FAIL_CLOSED ) } @@ -289,6 +291,7 @@ class PackageInstallerSessionTest { assertThat(expected.installerPackageName).isEqualTo(actual.installerPackageName) assertThat(expected.isMultiPackage).isEqualTo(actual.isMultiPackage) assertThat(expected.isStaged).isEqualTo(actual.isStaged) + assertThat(expected.forceVerification).isEqualTo(actual.forceVerification) } private fun assertEquals( @@ -339,6 +342,7 @@ class PackageInstallerSessionTest { assertThat(expected.childSessionIds).asList() .containsExactlyElementsIn(actual.childSessionIds.toList()) assertThat(expected.preVerifiedDomains).isEqualTo(actual.preVerifiedDomains) + assertThat(expected.verificationPolicy).isEqualTo(actual.verificationPolicy) } private fun assertInstallSourcesEquivalent(expected: InstallSource, actual: InstallSource) { diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerificationStatusTrackerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerificationStatusTrackerTest.java index fa076db456ec..787fb5a4e524 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerificationStatusTrackerTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerificationStatusTrackerTest.java @@ -62,7 +62,7 @@ public class VerificationStatusTrackerTest { .thenReturn(TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS) .thenReturn(TEST_REQUEST_START_TIME + TEST_MAX_TIMEOUT_DURATION_MILLIS - 100) .thenReturn(TEST_REQUEST_START_TIME + TEST_MAX_TIMEOUT_DURATION_MILLIS); - mTracker = new VerificationStatusTracker(TEST_PACKAGE_NAME, TEST_TIMEOUT_DURATION_MILLIS, + mTracker = new VerificationStatusTracker(TEST_TIMEOUT_DURATION_MILLIS, TEST_MAX_TIMEOUT_DURATION_MILLIS, mInjector); } diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java index be094b0152bc..24617984eaf7 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java @@ -16,6 +16,9 @@ package com.android.server.pm.verify.pkg; +import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; +import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_OPEN; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -92,6 +95,9 @@ public class VerifierControllerTest { private static final long TEST_TIMEOUT_DURATION_MILLIS = TimeUnit.MINUTES.toMillis(1); private static final long TEST_MAX_TIMEOUT_DURATION_MILLIS = TimeUnit.MINUTES.toMillis(10); + private static final long TEST_VERIFIER_CONNECTION_TIMEOUT_DURATION_MILLIS = + TimeUnit.SECONDS.toMillis(10); + private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; private final ArrayList<SharedLibraryInfo> mTestDeclaredLibraries = new ArrayList<>(); private final PersistableBundle mTestExtensionParams = new PersistableBundle(); @@ -116,7 +122,8 @@ public class VerifierControllerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mInjector.isVerifierInstalled(any(Computer.class), anyInt())).thenReturn(true); + when(mInjector.getVerifierPackageName(any(Computer.class), anyInt())).thenReturn( + TEST_VERIFIER_COMPONENT_NAME.getPackageName()); when(mInjector.getRemoteService( any(Computer.class), any(Context.class), anyInt(), any(Handler.class) )).thenReturn(new Pair<>(mMockServiceConnector, TEST_VERIFIER_COMPONENT_NAME)); @@ -124,6 +131,9 @@ public class VerifierControllerTest { TEST_TIMEOUT_DURATION_MILLIS); when(mInjector.getMaxVerificationExtendedTimeoutMillis()).thenReturn( TEST_MAX_TIMEOUT_DURATION_MILLIS); + when(mInjector.getVerifierConnectionTimeoutMillis()).thenReturn( + TEST_VERIFIER_CONNECTION_TIMEOUT_DURATION_MILLIS + ); // Mock time forward as the code continues to check for the current time when(mInjector.getCurrentTimeMillis()) .thenReturn(TEST_REQUEST_START_TIME) @@ -150,21 +160,21 @@ public class VerifierControllerTest { @Test public void testVerifierNotInstalled() { - when(mInjector.isVerifierInstalled(any(Computer.class), anyInt())).thenReturn(false); + when(mInjector.getVerifierPackageName(any(Computer.class), anyInt())).thenReturn(null); when(mInjector.getRemoteService( any(Computer.class), any(Context.class), anyInt(), any(Handler.class) )).thenReturn(null); - assertThat(mVerifierController.isVerifierInstalled(mSnapshotSupplier, 0)).isFalse(); + assertThat(mVerifierController.getVerifierPackageName(mSnapshotSupplier, 0)).isNull(); assertThat(mVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0)) .isFalse(); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isFalse(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isFalse(); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ true)).isFalse(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ true)).isFalse(); verifyZeroInteractions(mSessionCallback); } @@ -176,7 +186,7 @@ public class VerifierControllerTest { @Test public void testVerifierAvailableButNotConnected() { - assertThat(mVerifierController.isVerifierInstalled(mSnapshotSupplier, 0)).isTrue(); + assertThat(mVerifierController.getVerifierPackageName(mSnapshotSupplier, 0)).isNotNull(); when(mInjector.getRemoteService( any(Computer.class), any(Context.class), anyInt(), any(Handler.class) )).thenReturn(null); @@ -200,24 +210,24 @@ public class VerifierControllerTest { ServiceConnector.ServiceLifecycleCallbacks<IVerifierService> callbacks = captor.getValue(); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); verify(mMockService, times(1)).onVerificationRequired(any(VerificationSession.class)); callbacks.onBinderDied(); // Test that nothing crashes if the service connection is lost - assertThat(mVerifierController.isVerifierInstalled(mSnapshotSupplier, 0)).isTrue(); + assertThat(mVerifierController.getVerifierPackageName(mSnapshotSupplier, 0)).isNotNull(); mVerifierController.notifyPackageNameAvailable(TEST_PACKAGE_NAME); mVerifierController.notifyVerificationCancelled(TEST_PACKAGE_NAME); mVerifierController.notifyVerificationTimeout(TEST_ID); verifyNoMoreInteractions(mMockService); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ true)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ true)).isTrue(); mVerifierController.notifyVerificationTimeout(TEST_ID); verify(mMockService, times(1)).onVerificationTimeout(eq(TEST_ID)); } @@ -226,8 +236,8 @@ public class VerifierControllerTest { public void testNotifyPackageNameAvailable() throws Exception { assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); mVerifierController.notifyPackageNameAvailable(TEST_PACKAGE_NAME); verify(mMockService).onPackageNameAvailable(eq(TEST_PACKAGE_NAME)); } @@ -236,8 +246,8 @@ public class VerifierControllerTest { public void testNotifyVerificationCancelled() throws Exception { assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); mVerifierController.notifyVerificationCancelled(TEST_PACKAGE_NAME); verify(mMockService).onVerificationCancelled(eq(TEST_PACKAGE_NAME)); } @@ -248,8 +258,8 @@ public class VerifierControllerTest { ArgumentCaptor.forClass(VerificationSession.class); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); verify(mMockService).onVerificationRequired(captor.capture()); VerificationSession session = captor.getValue(); assertThat(session.getId()).isEqualTo(TEST_ID); @@ -276,8 +286,8 @@ public class VerifierControllerTest { ArgumentCaptor.forClass(VerificationSession.class); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ true)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ true)).isTrue(); verify(mMockService).onVerificationRetry(captor.capture()); VerificationSession session = captor.getValue(); assertThat(session.getId()).isEqualTo(TEST_ID); @@ -302,8 +312,8 @@ public class VerifierControllerTest { public void testNotifyVerificationTimeout() throws Exception { assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ true)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ true)).isTrue(); mVerifierController.notifyVerificationTimeout(TEST_ID); verify(mMockService).onVerificationTimeout(eq(TEST_ID)); } @@ -320,8 +330,8 @@ public class VerifierControllerTest { ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); verify(mHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong()); verify(mSessionCallback, times(1)).onTimeout(); verify(mInjector, times(2)).getCurrentTimeMillis(); @@ -339,8 +349,8 @@ public class VerifierControllerTest { .thenAnswer(i -> true); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); verify(mHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong()); verify(mSessionCallback, times(1)).onTimeout(); verify(mInjector, times(2)).getCurrentTimeMillis(); @@ -350,8 +360,8 @@ public class VerifierControllerTest { ArgumentCaptor.forClass(VerificationSession.class); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ true)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ true)).isTrue(); verify(mMockService).onVerificationRetry(captor.capture()); VerificationSession session = captor.getValue(); VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build(); @@ -367,8 +377,8 @@ public class VerifierControllerTest { ArgumentCaptor.forClass(VerificationSession.class); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); verify(mMockService).onVerificationRequired(captor.capture()); VerificationSession session = captor.getValue(); session.reportVerificationIncomplete(VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN); @@ -383,8 +393,8 @@ public class VerifierControllerTest { ArgumentCaptor.forClass(VerificationSession.class); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); verify(mMockService).onVerificationRequired(captor.capture()); VerificationSession session = captor.getValue(); VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build(); @@ -401,8 +411,8 @@ public class VerifierControllerTest { ArgumentCaptor.forClass(VerificationSession.class); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); verify(mMockService).onVerificationRequired(captor.capture()); VerificationSession session = captor.getValue(); VerificationStatus status = new VerificationStatus.Builder() @@ -421,8 +431,8 @@ public class VerifierControllerTest { ArgumentCaptor.forClass(VerificationSession.class); assertThat(mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false)).isTrue(); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false)).isTrue(); verify(mMockService).onVerificationRequired(captor.capture()); VerificationSession session = captor.getValue(); VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build(); @@ -439,8 +449,8 @@ public class VerifierControllerTest { ArgumentCaptor.forClass(VerificationSession.class); mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false); verify(mMockService).onVerificationRequired(captor.capture()); VerificationSession session = captor.getValue(); final long initialTimeoutTime = TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS; @@ -456,8 +466,8 @@ public class VerifierControllerTest { ArgumentCaptor.forClass(VerificationSession.class); mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false); verify(mMockService).onVerificationRequired(captor.capture()); VerificationSession session = captor.getValue(); final long initialTimeoutTime = TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS; @@ -493,10 +503,27 @@ public class VerifierControllerTest { .thenReturn(TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS + 1); mVerifierController.startVerificationSession( mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, - TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback, - /* retry= */ false); + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false); verify(mHandler, times(3)).sendMessageAtTime(any(Message.class), anyLong()); verify(mInjector, times(6)).getCurrentTimeMillis(); verify(mSessionCallback, times(1)).onTimeout(); } + + @Test + public void testPolicyOverride() throws Exception { + ArgumentCaptor<VerificationSession> captor = + ArgumentCaptor.forClass(VerificationSession.class); + mVerifierController.startVerificationSession( + mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI, + TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams, + mSessionCallback, /* retry= */ false); + verify(mMockService).onVerificationRequired(captor.capture()); + VerificationSession session = captor.getValue(); + final int policy = VERIFICATION_POLICY_BLOCK_FAIL_OPEN; + when(mSessionCallback.setVerificationPolicy(eq(policy))).thenReturn(true); + assertThat(session.setVerificationPolicy(policy)).isTrue(); + assertThat(session.getVerificationPolicy()).isEqualTo(policy); + verify(mSessionCallback, times(1)).setVerificationPolicy(eq(policy)); + } } diff --git a/services/tests/appfunctions/Android.bp b/services/tests/appfunctions/Android.bp index c841643c6654..836f90b992d6 100644 --- a/services/tests/appfunctions/Android.bp +++ b/services/tests/appfunctions/Android.bp @@ -36,7 +36,9 @@ android_test { "androidx.test.core", "androidx.test.runner", "androidx.test.ext.truth", + "androidx.core_core-ktx", "kotlin-test", + "kotlinx_coroutines_test", "platform-test-annotations", "services.appfunctions", "servicestests-core-utils", diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt new file mode 100644 index 000000000000..a69e9025bfa0 --- /dev/null +++ b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appfunctions + +import android.app.appfunctions.flags.Flags +import android.content.Context +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER) +class AppFunctionManagerServiceImplTest { + @get:Rule + val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + + private val context: Context + get() = ApplicationProvider.getApplicationContext() + + private val serviceImpl = AppFunctionManagerServiceImpl(context) + + @Test + fun testGetLockForPackage_samePackage() { + val packageName = "com.example.app" + val lock1 = serviceImpl.getLockForPackage(packageName) + val lock2 = serviceImpl.getLockForPackage(packageName) + + // Assert that the same lock object is returned for the same package name + assertThat(lock1).isEqualTo(lock2) + } + + @Test + fun testGetLockForPackage_differentPackages() { + val packageName1 = "com.example.app1" + val packageName2 = "com.example.app2" + val lock1 = serviceImpl.getLockForPackage(packageName1) + val lock2 = serviceImpl.getLockForPackage(packageName2) + + // Assert that different lock objects are returned for different package names + assertThat(lock1).isNotEqualTo(lock2) + } + + @Ignore("Hard to deterministically trigger the garbage collector.") + @Test + fun testWeakReference_garbageCollected_differentLockAfterGC() = runTest { + // Create a large number of temporary objects to put pressure on the GC + val tempObjects = MutableList<Any?>(10000000) { Any() } + var callingPackage: String? = "com.example.app" + var lock1: Any? = serviceImpl.getLockForPackage(callingPackage) + callingPackage = null // Set the key to null + val lock1Hash = lock1.hashCode() + lock1 = null + + // Create memory pressure + repeat(3) { + for (i in 1..100) { + "a".repeat(10000) + } + System.gc() // Suggest garbage collection + System.runFinalization() + } + // Get the lock again - it should be a different object now + val lock2 = serviceImpl.getLockForPackage("com.example.app") + // Assert that the lock objects are different + assertThat(lock1Hash).isNotEqualTo(lock2.hashCode()) + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index 3b5a386dd303..b917af4d796e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -1658,6 +1658,70 @@ public class DisplayManagerServiceTest { } @Test + public void testGetDisplayIdsByGroupsIds() throws Exception { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + DisplayManagerInternal localService = displayManager.new LocalService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + // Create display 1 + FakeDisplayDevice displayDevice1 = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + LogicalDisplay display1 = logicalDisplayMapper.getDisplayLocked(displayDevice1); + final int groupId1 = display1.getDisplayInfoLocked().displayGroupId; + // Create display 2 + FakeDisplayDevice displayDevice2 = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + LogicalDisplay display2 = logicalDisplayMapper.getDisplayLocked(displayDevice2); + final int groupId2 = display2.getDisplayInfoLocked().displayGroupId; + // Both displays should be in the same display group + assertEquals(groupId1, groupId2); + final int[] displayIds = new int[]{ + display1.getDisplayIdLocked(), display2.getDisplayIdLocked()}; + final SparseArray<int[]> expectedDisplayGroups = new SparseArray<>(); + expectedDisplayGroups.put(groupId1, displayIds); + + final SparseArray<int[]> displayGroups = localService.getDisplayIdsByGroupsIds(); + + for (int i = 0; i < expectedDisplayGroups.size(); i++) { + final int groupId = expectedDisplayGroups.keyAt(i); + assertTrue(displayGroups.contains(groupId)); + assertArrayEquals(expectedDisplayGroups.get(groupId), displayGroups.get(groupId)); + } + } + + @Test + public void testGetDisplayIdsByGroupsIds_multipleDisplayGroups() throws Exception { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + DisplayManagerInternal localService = displayManager.new LocalService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + // Create display 1 + FakeDisplayDevice displayDevice1 = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + LogicalDisplay display1 = logicalDisplayMapper.getDisplayLocked(displayDevice1); + final int groupId1 = display1.getDisplayInfoLocked().displayGroupId; + // Create display 2 + FakeDisplayDevice displayDevice2 = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL); + LogicalDisplay display2 = logicalDisplayMapper.getDisplayLocked(displayDevice2); + final int groupId2 = display2.getDisplayInfoLocked().displayGroupId; + // Both displays should be in different display groups + assertNotEquals(groupId1, groupId2); + final SparseArray<int[]> expectedDisplayGroups = new SparseArray<>(); + expectedDisplayGroups.put(groupId1, new int[]{display1.getDisplayIdLocked()}); + expectedDisplayGroups.put(groupId2, new int[]{display2.getDisplayIdLocked()}); + + final SparseArray<int[]> displayGroups = localService.getDisplayIdsByGroupsIds(); + + assertEquals(expectedDisplayGroups.size(), displayGroups.size()); + for (int i = 0; i < expectedDisplayGroups.size(); i++) { + final int groupId = expectedDisplayGroups.keyAt(i); + assertTrue(displayGroups.contains(groupId)); + assertArrayEquals(expectedDisplayGroups.get(groupId), displayGroups.get(groupId)); + } + } + + @Test public void testCreateVirtualDisplay_isValidProjection_notValid() throws RemoteException { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 01b2d3e34bdc..fdf6b809fa85 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -2343,6 +2343,41 @@ public final class DisplayPowerControllerTest { } } + @Test + public void stylusUsageStarted_disablesAutomaticBrightnessStrategy() { + when(mDisplayManagerFlagsMock.isBlockAutobrightnessChangesOnStylusUsage()) + .thenReturn(true); + when(mDisplayManagerFlagsMock.isRefactorDisplayPowerControllerEnabled()) + .thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + advanceTime(2); + clearInvocations(mHolder.automaticBrightnessController); + mHolder.dpc.stylusGestureStarted(2000000); + + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + verify(mHolder.automaticBrightnessController, times(0)) + .getAutomaticScreenBrightness(any()); + + // Stylus usage timed out, hence autobrightness is now enabled back again + advanceTime(6); + verify(mHolder.automaticBrightnessController).getAutomaticScreenBrightness(null); + + // Ideally we should be able to assert against new BrightnessEvent(Display.DEFAULT_DISPLAY), + // but because brightnessEvent has the mTime field which refers to the current time, + // asserting against that is non-trivial + verify(mHolder.automaticBrightnessController).getAutomaticScreenBrightness( + any(BrightnessEvent.class)); + } + /** * Creates a mock and registers it to {@link LocalServices}. */ @@ -2406,6 +2441,7 @@ public final class DisplayPowerControllerTest { .thenReturn(new int[0]); when(displayDeviceConfigMock.getDefaultDozeBrightness()) .thenReturn(DEFAULT_DOZE_BRIGHTNESS); + when(displayDeviceConfigMock.getIdleStylusTimeoutMillis()).thenReturn(5); when(displayDeviceConfigMock.getBrightnessRampFastDecrease()) .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java index 04b79b4f1761..d93ee84f4870 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java @@ -74,7 +74,7 @@ public final class BrightnessReasonTest { @Test public void setModifierDoesntSetIfModifierIsBeyondExtremes() { - int extremeModifier = 0x40; // equal to BrightnessReason.MODIFIER_MASK * 2 + int extremeModifier = 0x80; // reset modifier mBrightnessReason.setModifier(0); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java index c0698756a3d7..b3baa5deb4a7 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java @@ -125,7 +125,7 @@ public final class DisplayBrightnessControllerTest { DisplayManagerInternal.DisplayOffloadSession.class)); verify(displayBrightnessStrategy).updateBrightness( eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS, - /* userSetBrightnessChanged= */ false))); + /* userSetBrightnessChanged= */ false, /* isStylusBeingUsed */ false))); assertEquals(mDisplayBrightnessController.getCurrentDisplayBrightnessStrategy(), displayBrightnessStrategy); } @@ -559,4 +559,11 @@ public final class DisplayBrightnessControllerTest { displayDeviceConfig, handler, brightnessMappingStrategy, isDisplayEnabled, leadDisplayId); } + + @Test + public void setStylusBeingUsed_setsStylusInUseState() { + assertFalse(mDisplayBrightnessController.isStylusBeingUsed()); + mDisplayBrightnessController.setStylusBeingUsed(true); + assertTrue(mDisplayBrightnessController.isStylusBeingUsed()); + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java index a44c517ed9cf..fe1505162e24 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java @@ -68,6 +68,8 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public final class DisplayBrightnessStrategySelectorTest { private static final boolean DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING = false; + private static final boolean STYLUS_IS_NOT_BEING_USED = false; + private static final boolean STYLUS_IS_BEING_USED = true; private static final int DISPLAY_ID = 1; @Mock @@ -196,7 +198,8 @@ public final class DisplayBrightnessStrategySelectorTest { DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mDozeBrightnessModeStrategy); } @@ -212,7 +215,8 @@ public final class DisplayBrightnessStrategySelectorTest { DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING); assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mDozeBrightnessModeStrategy); } @@ -226,7 +230,8 @@ public final class DisplayBrightnessStrategySelectorTest { DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING); assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mDozeBrightnessModeStrategy); } @@ -249,7 +254,8 @@ public final class DisplayBrightnessStrategySelectorTest { assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mDozeBrightnessModeStrategy); } @@ -259,7 +265,8 @@ public final class DisplayBrightnessStrategySelectorTest { DisplayManagerInternal.DisplayPowerRequest.class); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_OFF, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mScreenOffBrightnessModeStrategy); } @@ -271,7 +278,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mOverrideBrightnessStrategy); } @@ -284,7 +292,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(0.3f); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mTemporaryBrightnessStrategy); } @@ -298,7 +307,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mBoostBrightnessStrategy); } @@ -312,7 +322,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mInvalidBrightnessStrategy); } @@ -323,7 +334,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(0.3f); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mFollowerBrightnessStrategy); } @@ -341,7 +353,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mOffloadBrightnessStrategy); } @@ -365,7 +378,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mAutomaticBrightnessStrategy); verify(mAutomaticBrightnessStrategy).setAutoBrightnessState(Display.STATE_ON, true, BrightnessReason.REASON_UNKNOWN, @@ -373,6 +387,32 @@ public final class DisplayBrightnessStrategySelectorTest { /* useNormalBrightnessForDoze= */ false, 0.1f, false); } + + @Test + public void selectStrategy_doesNotSelectAutomaticStrategyWhenStylusInUse() { + when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true); + when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); + when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + true); + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( + DisplayManagerInternal.DisplayPowerRequest.class); + displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; + displayPowerRequest.screenBrightnessOverride = Float.NaN; + when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN); + when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN); + when(mAutomaticBrightnessStrategy.shouldUseAutoBrightness()).thenReturn(true); + when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true); + assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( + new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_BEING_USED)), + mAutomaticBrightnessStrategy); + } + @Test public void selectStrategy_selectsAutomaticFallbackStrategyWhenValid() { when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true); @@ -389,7 +429,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mAutoBrightnessFallbackStrategy.isValid()).thenReturn(true); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mAutoBrightnessFallbackStrategy); } @@ -407,7 +448,8 @@ public final class DisplayBrightnessStrategySelectorTest { assertNotEquals(mOffloadBrightnessStrategy, mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession))); + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED))); } @Test @@ -425,7 +467,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(false); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mFallbackBrightnessStrategy); } @@ -440,7 +483,8 @@ public final class DisplayBrightnessStrategySelectorTest { mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)); + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)); StrategySelectionNotifyRequest strategySelectionNotifyRequest = new StrategySelectionNotifyRequest(displayPowerRequest, Display.STATE_ON, diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java index 99dfa739fb80..2a71af06e0c2 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java @@ -129,7 +129,8 @@ public class AutoBrightnessFallbackStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mAutoBrightnessFallbackStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index efa8b3ef775f..8a1f86093ecf 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -637,7 +637,7 @@ public class AutomaticBrightnessStrategyTest { .build(); DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, - /* userSetBrightnessChanged= */ true)); + /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false)); assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); } @@ -686,7 +686,7 @@ public class AutomaticBrightnessStrategyTest { .build(); DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, - /* userSetBrightnessChanged= */ true)); + /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false)); assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); } @@ -725,7 +725,7 @@ public class AutomaticBrightnessStrategyTest { .build(); DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, - /* userSetBrightnessChanged= */ true)); + /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false)); assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); } @@ -764,7 +764,7 @@ public class AutomaticBrightnessStrategyTest { .build(); DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, - /* userSetBrightnessChanged= */ true)); + /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false)); assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java index 275bb3efee8e..c03309e8b4a4 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java @@ -60,7 +60,8 @@ public class BoostBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mBoostBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java index 23e447c25245..e7f80b04e669 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java @@ -57,7 +57,8 @@ public class DozeBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mDozeBrightnessModeStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java index c4a579092d38..dcfa174a53f5 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java @@ -61,7 +61,8 @@ public class FallbackBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mFallbackBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, currentBrightness, - /* userSetBrightnessChanged= */ true)); + /* userSetBrightnessChanged= */ true, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java index c01f96e800de..239cdb6002e9 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java @@ -61,7 +61,8 @@ public class FollowerBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mFollowerBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(expectedDisplayBrightnessState, updatedDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java index 9fb2afa26ed2..77302f8747c1 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java @@ -72,7 +72,8 @@ public class OffloadBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mOffloadBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, mOffloadBrightnessStrategy .getOffloadScreenBrightness(), 0.0f); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java index e8b4c06b9c89..cc21af19722b 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java @@ -60,7 +60,8 @@ public class OverrideBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mOverrideBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java index 38709ece7007..652663e52a0a 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java @@ -58,7 +58,8 @@ public final class ScreenOffBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mScreenOffBrightnessModeStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java index f523b6af426b..0022cab371e8 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java @@ -60,7 +60,8 @@ public class TemporaryBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mTemporaryBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java index 121145672d68..439243e85e75 100644 --- a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java +++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java @@ -82,6 +82,11 @@ public class AudioManagerRouteControllerTest { private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET = createAudioDeviceInfo( AudioSystem.DEVICE_OUT_WIRED_HEADSET, "name_wired_hs", /* address= */ null); + private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS = + createAudioDeviceInfo( + AudioSystem.DEVICE_OUT_WIRED_HEADSET, + "name_wired_hs_with_address", + /* address= */ "card=1;device=0"); private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP = createAudioDeviceInfo( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "name_a2dp", /* address= */ "12:34:45"); @@ -304,6 +309,55 @@ public class AudioManagerRouteControllerTest { assertThat(selectedRoute.getName().toString()).isEqualTo(FAKE_ROUTE_NAME); } + @Test + public void getAvailableRoutes_whenAddressIsPopulatedForNonBluetoothDevice_usesCorrectName() { + addAvailableAudioDeviceInfo( + /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS, + /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS, + FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP); + + List<MediaRoute2Info> availableRoutes = mControllerUnderTest.getAvailableRoutes(); + assertThat(availableRoutes.size()).isEqualTo(3); + + assertThat( + getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET) + .getName() + .toString()) + .isEqualTo( + FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS + .getProductName() + .toString()); + + assertThat( + getAvailableRouteWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP) + .getName() + .toString()) + .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP.getProductName().toString()); + } + + @Test + public void + getAvailableRoutes_whenAddressIsNotPopulatedForNonBluetoothDevice_usesCorrectName() { + addAvailableAudioDeviceInfo( + /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET, + /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET); + + List<MediaRoute2Info> availableRoutes = mControllerUnderTest.getAvailableRoutes(); + assertThat(availableRoutes.size()).isEqualTo(2); + + assertThat( + getAvailableRouteWithType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER) + .getName() + .toString()) + .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER.getProductName().toString()); + + assertThat( + getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET) + .getName() + .toString()) + .isEqualTo(FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET.getProductName().toString()); + } + // Internal methods. @NonNull diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java index 6ccc03709b4f..2a825f35bf62 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -107,6 +107,7 @@ import android.os.IBinder; import android.os.IProgressListener; import android.os.Looper; import android.os.Message; +import android.os.Parcel; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; @@ -1309,12 +1310,13 @@ public class ActivityManagerServiceTest { intent.putExtra("EXTRA_INTENT0", extraIntent); intent.collectExtraIntentKeys(); - mAms.addCreatorToken(intent); + mAms.addCreatorToken(intent, TEST_PACKAGE); ActivityManagerService.IntentCreatorToken token = (ActivityManagerService.IntentCreatorToken) extraIntent.getCreatorToken(); assertThat(token).isNotNull(); assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid()); + assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE); } @Test @@ -1330,7 +1332,7 @@ public class ActivityManagerServiceTest { fillinIntent.collectExtraIntentKeys(); intent.fillIn(fillinIntent, FILL_IN_ACTION); - mAms.addCreatorToken(fillinIntent); + mAms.addCreatorToken(fillinIntent, TEST_PACKAGE); fillinExtraIntent = intent.getParcelableExtra("FILLIN_EXTRA_INTENT0", Intent.class); @@ -1338,6 +1340,49 @@ public class ActivityManagerServiceTest { (ActivityManagerService.IntentCreatorToken) fillinExtraIntent.getCreatorToken(); assertThat(token).isNotNull(); assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid()); + assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_PREVENT_INTENT_REDIRECT) + public void testCheckCreatorToken() { + Intent intent = new Intent(); + Intent extraIntent = new Intent("EXTRA_INTENT_ACTION"); + intent.putExtra("EXTRA_INTENT", extraIntent); + + intent.collectExtraIntentKeys(); + + // mimic client hack and sneak in an extra intent without going thru collectExtraIntentKeys. + Intent extraIntent2 = new Intent("EXTRA_INTENT_ACTION2"); + intent.putExtra("EXTRA_INTENT2", extraIntent2); + + // mock parceling on the client side, unparcling on the system server side, then + // addCreatorToken on system server side. + final Parcel parcel = Parcel.obtain(); + intent.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + Intent newIntent = new Intent(); + newIntent.readFromParcel(parcel); + intent = newIntent; + mAms.addCreatorToken(intent, TEST_PACKAGE); + // entering the target app's process. + intent.checkCreatorToken(); + + Intent extraIntent3 = new Intent("EXTRA_INTENT_ACTION3"); + intent.putExtra("EXTRA_INTENT3", extraIntent3); + + extraIntent = intent.getParcelableExtra("EXTRA_INTENT", Intent.class); + extraIntent2 = intent.getParcelableExtra("EXTRA_INTENT2", Intent.class); + extraIntent3 = intent.getParcelableExtra("EXTRA_INTENT3", Intent.class); + + assertThat(extraIntent.getExtendedFlags() + & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0); + // sneaked in intent should have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set. + assertThat(extraIntent2.getExtendedFlags() + & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isNotEqualTo(0); + // local created intent should not have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set. + assertThat(extraIntent3.getExtendedFlags() + & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0); } private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq, diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 31157f9e27dc..5c718d982476 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -25,7 +25,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.job.Flags.FLAG_COUNT_QUOTA_FIX; -import static com.android.server.job.Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS; import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; @@ -77,9 +76,12 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.SparseBooleanArray; @@ -90,12 +92,12 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.PowerAllowlistInternal; +import com.android.server.job.Flags; import com.android.server.job.JobSchedulerInternal; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobStore; import com.android.server.job.controllers.QuotaController.ExecutionStats; import com.android.server.job.controllers.QuotaController.QcConstants; -import com.android.server.job.controllers.QuotaController.QuotaBump; import com.android.server.job.controllers.QuotaController.ShrinkableDebits; import com.android.server.job.controllers.QuotaController.TimedEvent; import com.android.server.job.controllers.QuotaController.TimingSession; @@ -132,11 +134,11 @@ public class QuotaControllerTest { private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests"; private static final int SOURCE_USER_ID = 0; + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private QuotaController mQuotaController; private QuotaController.QcConstants mQcConstants; private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants(); private int mSourceUid; - private AppStandbyInternal.AppIdleStateChangeListener mAppIdleStateChangeListener; private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener; private IUidObserver mUidObserver; private UsageStatsManagerInternal.UsageEventListener mUsageEventListener; @@ -191,8 +193,7 @@ public class QuotaControllerTest { when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager); doReturn(mActivityMangerInternal) .when(() -> LocalServices.getService(ActivityManagerInternal.class)); - final AppStandbyInternal appStandbyInternal = mock(AppStandbyInternal.class); - doReturn(appStandbyInternal) + doReturn(mock(AppStandbyInternal.class)) .when(() -> LocalServices.getService(AppStandbyInternal.class)); doReturn(mock(BatteryManagerInternal.class)) .when(() -> LocalServices.getService(BatteryManagerInternal.class)); @@ -239,8 +240,6 @@ public class QuotaControllerTest { // Initialize real objects. // Capture the listeners. - ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> aiscListenerCaptor = - ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class); ArgumentCaptor<IUidObserver> uidObserverCaptor = ArgumentCaptor.forClass(IUidObserver.class); ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor = @@ -250,8 +249,6 @@ public class QuotaControllerTest { mQuotaController = new QuotaController(mJobSchedulerService, mock(BackgroundJobsController.class), mock(ConnectivityController.class)); - verify(appStandbyInternal).addListener(aiscListenerCaptor.capture()); - mAppIdleStateChangeListener = aiscListenerCaptor.getValue(); verify(mPowerAllowlistInternal) .registerTempAllowlistChangeListener(taChangeCaptor.capture()); mTempAllowlistListener = taChangeCaptor.getValue(); @@ -488,14 +485,12 @@ public class QuotaControllerTest { now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3); TimingSession two = createTimingSession( now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); - QuotaBump bump1 = new QuotaBump(now - 2 * HOUR_IN_MILLIS); TimingSession thr = createTimingSession( now - (3 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); // Overlaps 24 hour boundary. TimingSession fou = createTimingSession( now - (24 * HOUR_IN_MILLIS + 2 * MINUTE_IN_MILLIS), 7 * MINUTE_IN_MILLIS, 1); // Way past the 24 hour boundary. - QuotaBump bump2 = new QuotaBump(now - 24 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); TimingSession fiv = createTimingSession( now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4); List<TimedEvent> expectedRegular = new ArrayList<>(); @@ -503,16 +498,13 @@ public class QuotaControllerTest { // Added in correct (chronological) order. expectedRegular.add(fou); expectedRegular.add(thr); - expectedRegular.add(bump1); expectedRegular.add(two); expectedRegular.add(one); expectedEJ.add(fou); expectedEJ.add(one); mQuotaController.saveTimingSession(0, "com.android.test", fiv, false); - mQuotaController.getTimingSessions(0, "com.android.test").add(bump2); mQuotaController.saveTimingSession(0, "com.android.test", fou, false); mQuotaController.saveTimingSession(0, "com.android.test", thr, false); - mQuotaController.getTimingSessions(0, "com.android.test").add(bump1); mQuotaController.saveTimingSession(0, "com.android.test", two, false); mQuotaController.saveTimingSession(0, "com.android.test", one, false); mQuotaController.saveTimingSession(0, "com.android.test", fiv, true); @@ -942,7 +934,8 @@ public class QuotaControllerTest { * Tests that getExecutionStatsLocked returns the correct stats. */ @Test - public void testGetExecutionStatsLocked_Values() { + @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetExecutionStatsLocked_Values_LegacyDefaultBucketWindowSizes() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - HOUR_IN_MILLIS), @@ -1027,11 +1020,112 @@ public class QuotaControllerTest { } } + @Test + @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); + + ExecutionStats expectedStats = new ExecutionStats(); + + // Exempted + expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS; + expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED; + expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED; + expectedStats.expirationTimeElapsed = now + 14 * MINUTE_IN_MILLIS; + expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS; + expectedStats.bgJobCountInWindow = 5; + expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; + expectedStats.bgJobCountInMaxPeriod = 20; + expectedStats.sessionCountInWindow = 1; + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + EXEMPTED_INDEX)); + } + + // Active + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS; + expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE; + expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE; + // There is only one session in the past active bucket window, the empty time for this + // window is the bucket window size - duration of the session. + expectedStats.expirationTimeElapsed = now + 24 * MINUTE_IN_MILLIS; + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + ACTIVE_INDEX)); + } + + // Working + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS; + expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING; + expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING; + expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; + expectedStats.executionTimeInWindowMs = 13 * MINUTE_IN_MILLIS; + expectedStats.bgJobCountInWindow = 10; + expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; + expectedStats.bgJobCountInMaxPeriod = 20; + expectedStats.sessionCountInWindow = 2; + expectedStats.inQuotaTimeElapsed = now + 2 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS + + mQcConstants.IN_QUOTA_BUFFER_MS; + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + WORKING_INDEX)); + } + + // Frequent + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS; + expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT; + expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT; + expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; + expectedStats.executionTimeInWindowMs = 23 * MINUTE_IN_MILLIS; + expectedStats.bgJobCountInWindow = 15; + expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; + expectedStats.bgJobCountInMaxPeriod = 20; + expectedStats.sessionCountInWindow = 3; + expectedStats.inQuotaTimeElapsed = now + 10 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS + + mQcConstants.IN_QUOTA_BUFFER_MS; + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX)); + } + + // Rare + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS; + expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; + expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; + expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; + expectedStats.executionTimeInWindowMs = 33 * MINUTE_IN_MILLIS; + expectedStats.bgJobCountInWindow = 20; + expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; + expectedStats.bgJobCountInMaxPeriod = 20; + expectedStats.sessionCountInWindow = 4; + expectedStats.inQuotaTimeElapsed = now + 22 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS + + mQcConstants.IN_QUOTA_BUFFER_MS; + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + RARE_INDEX)); + } + } + /** * Tests that getExecutionStatsLocked returns the correct stats soon after device startup. */ @Test - public void testGetExecutionStatsLocked_Values_BeginningOfTime() { + @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetExecutionStatsLocked_Values_BeginningOfTime_LegacyDefaultBucketWindowSizes() { // Set time to 3 minutes after boot. advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis()); advanceElapsedClock(3 * MINUTE_IN_MILLIS); @@ -1054,7 +1148,8 @@ public class QuotaControllerTest { expectedStats.sessionCountInWindow = 1; synchronized (mQuotaController.mLock) { assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX)); + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + ACTIVE_INDEX)); } // Working @@ -1064,7 +1159,8 @@ public class QuotaControllerTest { expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; synchronized (mQuotaController.mLock) { assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX)); + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + WORKING_INDEX)); } // Frequent @@ -1085,7 +1181,83 @@ public class QuotaControllerTest { expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; synchronized (mQuotaController.mLock) { assertEquals(expectedStats, - mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + RARE_INDEX)); + } + } + + @Test + @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes() { + // Set time to 3 minutes after boot. + advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis()); + advanceElapsedClock(3 * MINUTE_IN_MILLIS); + + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false); + + ExecutionStats expectedStats = new ExecutionStats(); + + // Exempted + expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS; + expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE; + expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE; + expectedStats.expirationTimeElapsed = 10 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS; + expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS; + expectedStats.bgJobCountInWindow = 2; + expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS; + expectedStats.bgJobCountInMaxPeriod = 2; + expectedStats.sessionCountInWindow = 1; + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + EXEMPTED_INDEX)); + } + + // Active + expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS; + expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE; + expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE; + expectedStats.expirationTimeElapsed = 20 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS; + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + ACTIVE_INDEX)); + } + + // Working + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS; + expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING; + expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING; + expectedStats.expirationTimeElapsed = 4 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + WORKING_INDEX)); + } + + // Frequent + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS; + expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT; + expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT; + expectedStats.expirationTimeElapsed = 12 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX)); + } + + // Rare + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS; + expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; + expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; + expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; + synchronized (mQuotaController.mLock) { + assertEquals(expectedStats, + mQuotaController.getExecutionStatsLocked(0, "com.android.test", + RARE_INDEX)); } } @@ -1093,7 +1265,8 @@ public class QuotaControllerTest { * Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing. */ @Test - public void testGetExecutionStatsLocked_CoalescingSessions() { + @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetExecutionStatsLocked_CoalescingSessions_LegacyDefaultBucketWindowSizes() { for (int i = 0; i < 10; ++i) { mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession( @@ -1110,12 +1283,14 @@ public class QuotaControllerTest { advanceElapsedClock(54 * SECOND_IN_MILLIS); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession( - JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false); + JobSchedulerService.sElapsedRealtimeClock.millis(), + 500, 1), false); advanceElapsedClock(500); advanceElapsedClock(400); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession( - JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false); + JobSchedulerService.sElapsedRealtimeClock.millis(), + 100, 1), false); advanceElapsedClock(100); advanceElapsedClock(5 * SECOND_IN_MILLIS); } @@ -1241,6 +1416,164 @@ public class QuotaControllerTest { } } + @Test + @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes() { + for (int i = 0; i < 20; ++i) { + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession( + JobSchedulerService.sElapsedRealtimeClock.millis(), + 5 * MINUTE_IN_MILLIS, 5), false); + advanceElapsedClock(5 * MINUTE_IN_MILLIS); + advanceElapsedClock(5 * MINUTE_IN_MILLIS); + for (int j = 0; j < 5; ++j) { + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession( + JobSchedulerService.sElapsedRealtimeClock.millis(), + MINUTE_IN_MILLIS, 2), false); + advanceElapsedClock(MINUTE_IN_MILLIS); + advanceElapsedClock(54 * SECOND_IN_MILLIS); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession( + JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false); + advanceElapsedClock(500); + advanceElapsedClock(400); + mQuotaController.saveTimingSession(0, "com.android.test", + createTimingSession( + JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false); + advanceElapsedClock(100); + advanceElapsedClock(5 * SECOND_IN_MILLIS); + } + advanceElapsedClock(40 * MINUTE_IN_MILLIS); + } + + setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0); + + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(64, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(192, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(320, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } + + setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500); + + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + // WINDOW_SIZE_WORKING_MS * 5 TimingSessions are coalesced + assertEquals(44, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + // WINDOW_SIZE_FREQUENT_MS * 5 TimingSessions are coalesced + assertEquals(132, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + // WINDOW_SIZE_RARE_MS * 5 TimingSessions are coalesced + assertEquals(220, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } + + setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000); + + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(44, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(132, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(220, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } + + setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, + 5 * SECOND_IN_MILLIS); + + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + // WINDOW_SIZE_WORKING_MS * 9 TimingSessions are coalesced + assertEquals(28, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + // WINDOW_SIZE_FREQUENT_MS * 9 TimingSessions are coalesced + assertEquals(84, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + // WINDOW_SIZE_RARE_MS * 9 TimingSessions are coalesced + assertEquals(140, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } + + setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, + MINUTE_IN_MILLIS); + + // Only two TimingSessions there for every hour. + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(8, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(24, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(40, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } + + setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, + 5 * MINUTE_IN_MILLIS); + + // Only one TimingSessions there for every hour + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(4, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(12, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(20, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } + + setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, + 15 * MINUTE_IN_MILLIS); + + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(4, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(12, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(20, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } + + // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference + // between an hour and 15 minutes. + setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS); + + synchronized (mQuotaController.mLock) { + mQuotaController.invalidateAllExecutionStatsLocked(); + assertEquals(0, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); + assertEquals(4, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); + assertEquals(12, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); + assertEquals(20, mQuotaController.getExecutionStatsLocked( + 0, "com.android.test", RARE_INDEX).sessionCountInWindow); + } + } + /** * Tests that getExecutionStatsLocked properly caches the stats and returns the cached object. */ @@ -1466,7 +1799,8 @@ public class QuotaControllerTest { } @Test - public void testGetMaxJobExecutionTimeLocked_EJ() { + @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetMaxJobExecutionTimeLocked_EJ_LegacyDefaultEJLimits() { final long timeUsedMs = 3 * MINUTE_IN_MILLIS; mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), @@ -1542,11 +1876,91 @@ public class QuotaControllerTest { } } + @Test + @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetMaxJobExecutionTimeLocked_EJ_NewDefaultEJLimits() { + final long timeUsedMs = 3 * MINUTE_IN_MILLIS; + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), + timeUsedMs, 5), true); + JobStatus job = createExpeditedJobStatus("testGetMaxJobExecutionTimeLocked_EJ", 0); + setStandbyBucket(RARE_INDEX, job); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + } + + setCharging(); + synchronized (mQuotaController.mLock) { + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + + setDischarging(); + setProcessState(getProcessStateQuotaFreeThreshold()); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + + // Top-started job + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(job); + } + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + mQuotaController.maybeStopTrackingJobLocked(job, null); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + + // Test used quota rolling out of window. + synchronized (mQuotaController.mLock) { + mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); + } + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(sElapsedRealtimeClock.millis() - mQcConstants.EJ_WINDOW_SIZE_MS, + timeUsedMs, 5), true); + + setProcessState(getProcessStateQuotaFreeThreshold()); + synchronized (mQuotaController.mLock) { + // max of 50% WORKING limit and remaining quota + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + + // Top-started job + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + mQuotaController.prepareForExecutionLocked(job); + } + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + mQuotaController.maybeStopTrackingJobLocked(job, null); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + } + /** * Test getTimeUntilQuotaConsumedLocked when allowed time equals the bucket window size. */ @Test - public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow() { + @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_LegacyDefaultBucketWindowSizes() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false); @@ -1570,27 +1984,56 @@ public class QuotaControllerTest { } } + @Test + @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_NewDefaultBucketWindowSizes() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), + false); + + setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, + 20 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS); + // window size = allowed time, so jobs can essentially run non-stop until they reach the + // max execution time. + setStandbyBucket(EXEMPTED_INDEX); + synchronized (mQuotaController.mLock) { + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + /** * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket * window. */ @Test - public void testGetTimeUntilQuotaConsumedLocked_BucketWindow() { + @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_LegacyDefaultBucketWindowSizes() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); // Close to RARE boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS), + createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - 30 * SECOND_IN_MILLIS), 30 * SECOND_IN_MILLIS, 5), false); // Far away from FREQUENT boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); + createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS), + 3 * MINUTE_IN_MILLIS, 5), false); // Overlap WORKING_SET boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), + createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); // Close to ACTIVE boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); + createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS), + 3 * MINUTE_IN_MILLIS, 5), false); setStandbyBucket(RARE_INDEX); synchronized (mQuotaController.mLock) { @@ -1635,6 +2078,69 @@ public class QuotaControllerTest { } } + @Test + @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + // Close to RARE boundary. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - 30 * SECOND_IN_MILLIS), + 30 * SECOND_IN_MILLIS, 5), false); + // Far away from FREQUENT boundary. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS), + 3 * MINUTE_IN_MILLIS, 5), false); + // Overlap WORKING_SET boundary. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS + MINUTE_IN_MILLIS), + 3 * MINUTE_IN_MILLIS, 5), false); + // Close to ACTIVE boundary. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS), + 3 * MINUTE_IN_MILLIS, 5), false); + + setStandbyBucket(RARE_INDEX); + synchronized (mQuotaController.mLock) { + assertEquals(30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + setStandbyBucket(FREQUENT_INDEX); + synchronized (mQuotaController.mLock) { + assertEquals(MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + setStandbyBucket(WORKING_INDEX); + synchronized (mQuotaController.mLock) { + assertEquals(5 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(7 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + // ACTIVE window != allowed time. + setStandbyBucket(ACTIVE_INDEX); + synchronized (mQuotaController.mLock) { + assertEquals(7 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + /** * Test getTimeUntilQuotaConsumedLocked when the app is close to the max execution limit. */ @@ -1759,7 +2265,8 @@ public class QuotaControllerTest { * Test getTimeUntilQuotaConsumedLocked when allowed time equals the bucket window size. */ @Test - public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow() { + @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_LegacyDefaultBucketWindowSizes() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (24 * HOUR_IN_MILLIS), @@ -1785,55 +2292,84 @@ public class QuotaControllerTest { } } + @Test + @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_NewDefaultBucketWindowSizes() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (24 * HOUR_IN_MILLIS), + mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS, 5), + false); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (20 * MINUTE_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), + false); + + setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, + 20 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS); + // window size != allowed time. + setStandbyBucket(EXEMPTED_INDEX); + synchronized (mQuotaController.mLock) { + assertEquals(0, + mQuotaController.getRemainingExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS, + mQuotaController.getTimeUntilQuotaConsumedLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + /** * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket * window and the session is rolling out of the window. */ @Test - public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow() { + @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow_LegacyDefaultBucketWindowSizes() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (24 * HOUR_IN_MILLIS), - 10 * MINUTE_IN_MILLIS, 5), false); + createTimingSession(now - mQcConstants.WINDOW_SIZE_RARE_MS, + mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, 5), false); setStandbyBucket(RARE_INDEX); synchronized (mQuotaController.mLock) { assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(10 * MINUTE_IN_MILLIS, + assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, mQuotaController.getTimeUntilQuotaConsumedLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (8 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); + createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS, + mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 5), false); setStandbyBucket(FREQUENT_INDEX); synchronized (mQuotaController.mLock) { assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(10 * MINUTE_IN_MILLIS, + assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, mQuotaController.getTimeUntilQuotaConsumedLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (2 * HOUR_IN_MILLIS), - 10 * MINUTE_IN_MILLIS, 5), false); + createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, + mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, 5), false); setStandbyBucket(WORKING_INDEX); synchronized (mQuotaController.mLock) { assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(10 * MINUTE_IN_MILLIS, + assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, mQuotaController.getTimeUntilQuotaConsumedLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), - false); + createTimingSession(now - mQcConstants.WINDOW_SIZE_ACTIVE_MS, + mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 5), false); // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the // max execution time. setStandbyBucket(ACTIVE_INDEX); @@ -1841,130 +2377,85 @@ public class QuotaControllerTest { assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS, + assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS + - (mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS + + mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS + + mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS), mQuotaController.getTimeUntilQuotaConsumedLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } } - /** - * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket - * window and there are valid QuotaBumps in the history. - */ @Test - public void testGetTimeUntilQuotaConsumedLocked_QuotaBump() { - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - + @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) + public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow_NewDefaultBucketWindowSizes() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - // Close to RARE boundary. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS), - 30 * SECOND_IN_MILLIS, 5), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS)); - // Far away from FREQUENT boundary. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); - // Overlap WORKING_SET boundary. - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 2 * HOUR_IN_MILLIS)); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS, 5), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS)); - // Close to ACTIVE boundary. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - mQcConstants.WINDOW_SIZE_RARE_MS, + mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, 5), false); setStandbyBucket(RARE_INDEX); synchronized (mQuotaController.mLock) { - assertEquals(3 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(4 * MINUTE_IN_MILLIS, + assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, mQuotaController.getTimeUntilQuotaConsumedLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS, + mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 5), false); setStandbyBucket(FREQUENT_INDEX); synchronized (mQuotaController.mLock) { - assertEquals(4 * MINUTE_IN_MILLIS, + assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(4 * MINUTE_IN_MILLIS, + assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, mQuotaController.getTimeUntilQuotaConsumedLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, + mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, 5), false); setStandbyBucket(WORKING_INDEX); synchronized (mQuotaController.mLock) { - assertEquals(8 * MINUTE_IN_MILLIS, + assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(10 * MINUTE_IN_MILLIS, + assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, mQuotaController.getTimeUntilQuotaConsumedLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } - // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the - // max execution time. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - mQcConstants.WINDOW_SIZE_ACTIVE_MS, + mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 5), + false); + // ACTIVE window != allowed time. setStandbyBucket(ACTIVE_INDEX); synchronized (mQuotaController.mLock) { - assertEquals(10 * MINUTE_IN_MILLIS, + assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS, + assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, mQuotaController.getTimeUntilQuotaConsumedLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } - } - /** - * Test getTimeUntilQuotaConsumedLocked when there are valid QuotaBumps in recent history that - * provide enough additional quota to bridge gaps between sessions. - */ - @Test - public void testGetTimeUntilQuotaConsumedLocked_QuotaBump_CrucialBumps() { - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - - final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (25 * HOUR_IN_MILLIS), - 30 * MINUTE_IN_MILLIS, 25), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS)); - // Without the valid quota bumps, the app would only 3 minutes until the quota was consumed. - // The quota bumps provide enough quota to bridge the gap between the two earliest sessions. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (8 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (8 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS), - 2 * MINUTE_IN_MILLIS, 5), false); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS, 1), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS)); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (9 * MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 1), false); - - setStandbyBucket(FREQUENT_INDEX); + createTimingSession(now - mQcConstants.WINDOW_SIZE_EXEMPTED_MS, + mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, 5), + false); + // EXEMPTED window != allowed time + setStandbyBucket(EXEMPTED_INDEX); synchronized (mQuotaController.mLock) { - assertEquals(2 * MINUTE_IN_MILLIS, + assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(7 * MINUTE_IN_MILLIS, + assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, mQuotaController.getTimeUntilQuotaConsumedLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } @@ -2316,272 +2807,6 @@ public class QuotaControllerTest { } } - @Test - public void testIsWithinQuotaLocked_WithQuotaBump_Duration() { - setDischarging(); - int standbyBucket = WORKING_INDEX; - setStandbyBucket(standbyBucket); - setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, - 5 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5); - - long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession( - now - (HOUR_IN_MILLIS - 2 * MINUTE_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 1), - false); - final ExecutionStats stats; - synchronized (mQuotaController.mLock) { - stats = mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); - mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 1); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(5 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - advanceElapsedClock(HOUR_IN_MILLIS); - - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - // Emulate a quota bump while some jobs are executing - JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 1); - JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 2); - - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job1, null); - mQuotaController.prepareForExecutionLocked(job1); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - mQuotaController.maybeStartTrackingJobLocked(job2, null); - mQuotaController.prepareForExecutionLocked(job2); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job1, null); - mQuotaController.maybeStopTrackingJobLocked(job2, null); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - // Phase out the first session - advanceElapsedClock(5 * MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - // Phase out the first quota bump - advanceElapsedClock(7 * HOUR_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - } - - @Test - public void testIsWithinQuotaLocked_WithQuotaBump_JobCount() { - setDischarging(); - int standbyBucket = WORKING_INDEX; - setStandbyBucket(standbyBucket); - setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, - 20 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 1); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5); - - long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS - HOUR_IN_MILLIS), - 5 * MINUTE_IN_MILLIS, 10), false); - final ExecutionStats stats; - synchronized (mQuotaController.mLock) { - stats = mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); - mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 10); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(10, stats.jobCountLimit); - } - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(11, stats.jobCountLimit); - } - - advanceElapsedClock(HOUR_IN_MILLIS); - - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(11, stats.jobCountLimit); - } - - // Emulate a quota bump while some jobs are executing - JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1); - JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2); - - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job1, null); - mQuotaController.prepareForExecutionLocked(job1); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(12, stats.jobCountLimit); - mQuotaController.maybeStartTrackingJobLocked(job2, null); - mQuotaController.prepareForExecutionLocked(job2); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job1, null); - mQuotaController.maybeStopTrackingJobLocked(job2, null); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(12, stats.jobCountLimit); - } - - // Phase out the first session - advanceElapsedClock(3 * MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(12, stats.jobCountLimit); - } - - // Phase out the first quota bump - advanceElapsedClock(7 * HOUR_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(11, stats.jobCountLimit); - } - } - - @Test - public void testIsWithinQuotaLocked_WithQuotaBump_SessionCount() { - setDischarging(); - int standbyBucket = WORKING_INDEX; - setStandbyBucket(standbyBucket); - setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, - 20 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 2); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 1); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5); - - long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS - HOUR_IN_MILLIS), - 5 * MINUTE_IN_MILLIS, 1), false); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), false); - final ExecutionStats stats; - synchronized (mQuotaController.mLock) { - stats = mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(2, stats.sessionCountLimit); - } - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(3, stats.sessionCountLimit); - } - - advanceElapsedClock(HOUR_IN_MILLIS); - - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(3, stats.sessionCountLimit); - } - - // Emulate a quota bump while some jobs are executing - JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1); - JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2); - - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job1, null); - mQuotaController.prepareForExecutionLocked(job1); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job1, null); - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(4, stats.sessionCountLimit); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job2, null); - mQuotaController.prepareForExecutionLocked(job2); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job2, null); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(4, stats.sessionCountLimit); - } - - // Phase out the first session - advanceElapsedClock(2 * MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(4, stats.sessionCountLimit); - } - - // Phase out the first quota bump - advanceElapsedClock(7 * HOUR_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(3, stats.sessionCountLimit); - } - } @Test public void testIsWithinEJQuotaLocked_NeverApp() { @@ -3590,12 +3815,6 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 84 * SECOND_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - 93 * SECOND_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 92); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 91); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 90 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 89); assertEquals(8 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); @@ -3653,11 +3872,6 @@ public class QuotaControllerTest { assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs()); assertEquals(83 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs()); - assertEquals(93 * SECOND_IN_MILLIS, mQuotaController.getQuotaBumpAdditionDurationMs()); - assertEquals(92, mQuotaController.getQuotaBumpAdditionJobCount()); - assertEquals(91, mQuotaController.getQuotaBumpAdditionSessionCount()); - assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs()); - assertEquals(89, mQuotaController.getQuotaBumpLimit()); } @Test @@ -3710,11 +3924,6 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, -1); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, -1); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, -1); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 59 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, -1); assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); @@ -3765,11 +3974,6 @@ public class QuotaControllerTest { assertEquals(0, mQuotaController.getEJRewardNotificationSeenMs()); assertEquals(0, mQuotaController.getEJGracePeriodTempAllowlistMs()); assertEquals(0, mQuotaController.getEJGracePeriodTopAppMs()); - assertEquals(0, mQuotaController.getQuotaBumpAdditionDurationMs()); - assertEquals(0, mQuotaController.getQuotaBumpAdditionJobCount()); - assertEquals(0, mQuotaController.getQuotaBumpAdditionSessionCount()); - assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs()); - assertEquals(0, mQuotaController.getQuotaBumpLimit()); // Invalid configurations. // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD @@ -3827,7 +4031,6 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); @@ -3868,7 +4071,6 @@ public class QuotaControllerTest { assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs()); assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs()); - assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs()); } /** Tests that TimingSessions aren't saved when the device is charging. */ @@ -4157,7 +4359,7 @@ public class QuotaControllerTest { /** Tests that Timers count FOREGROUND_SERVICE jobs. */ @Test - @RequiresFlagsEnabled(FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS) + @EnableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS) public void testTimerTracking_Fgs() { setDischarging(); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java index 9ba272446689..625dbe6e16f9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.media.AudioAttributes.USAGE_ALARM; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; @@ -91,6 +92,7 @@ public class BackgroundUserSoundNotifierTest { } @Test public void testAlarmOnBackgroundUser_foregroundUserNotified() throws RemoteException { + assumeTrue(UserManager.supportsMultipleUsers()); AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build(); UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0); final int fgUserId = mSpiedContext.getUserId(); @@ -100,7 +102,7 @@ public class BackgroundUserSoundNotifierTest { /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); clearInvocations(mNotificationManager); - mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext); + mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi); verify(mNotificationManager) .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), eq(afi.getClientUid()), any(Notification.class), @@ -116,7 +118,7 @@ public class BackgroundUserSoundNotifierTest { /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); clearInvocations(mNotificationManager); - mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext); + mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi); verifyZeroInteractions(mNotificationManager); } @@ -131,7 +133,7 @@ public class BackgroundUserSoundNotifierTest { AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); clearInvocations(mNotificationManager); - mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext); + mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi); verifyZeroInteractions(mNotificationManager); } @@ -169,7 +171,7 @@ public class BackgroundUserSoundNotifierTest { doReturn(focusStack).when(mockAudioPolicy).getFocusStack(); mBackgroundUserSoundNotifier.mFocusControlAudioPolicy = mockAudioPolicy; - mBackgroundUserSoundNotifier.muteAlarmSounds(mSpiedContext); + mBackgroundUserSoundNotifier.muteAlarmSounds(bgUserUid); verify(apc1.getPlayerProxy()).stop(); verify(mockAudioPolicy).sendFocusLossAndUpdate(afi); @@ -178,6 +180,7 @@ public class BackgroundUserSoundNotifierTest { @Test public void testOnAudioFocusGrant_alarmOnBackgroundUser_notifiesForegroundUser() { + assumeTrue(UserManager.supportsMultipleUsers()); final int fgUserId = mSpiedContext.getUserId(); UserInfo bgUser = createUser("Background User", UserManager.USER_TYPE_FULL_SECONDARY, 0); int bgUserUid = bgUser.id * 100000; @@ -205,7 +208,7 @@ public class BackgroundUserSoundNotifierTest { .when(mUserManager).getUserSwitchability(any()); Notification notification = mBackgroundUserSoundNotifier.createNotification(userName, - mSpiedContext); + mSpiedContext, 101000); assertEquals("Alarm for BgUser", notification.extras.getString( Notification.EXTRA_TITLE)); @@ -232,7 +235,7 @@ public class BackgroundUserSoundNotifierTest { .when(mUserManager).getUserSwitchability(any()); Notification notification = mBackgroundUserSoundNotifier.createNotification(userName, - mSpiedContext); + mSpiedContext, 101000); assertEquals(1, notification.actions.length); assertEquals(mSpiedContext.getString( diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt index e131a98b52d0..de029e0d770f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt @@ -185,7 +185,8 @@ class DistractingPackageHelperTest : PackageHelperTestBase() { verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) verify(broadcastHelper, never()).sendPackageBroadcast(eq( Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(), - nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable()) + nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable(), + nullable()) distractingPackageHelper.removeDistractingPackageRestrictions(pms.snapshotComputer(), arrayOfNulls(0), TEST_USER_ID) diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java index 43a8aa957fa5..124c41ed449f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java @@ -741,7 +741,8 @@ public class StagingManagerTest { /* stagedSessionErrorCode */ PackageManager.INSTALL_UNKNOWN, /* stagedSessionErrorMessage */ "no error", /* preVerifiedDomains */ null, - /* verifierController */ null); + /* verifierController */ null, + /* verificationPolicy */ PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED); StagingManager.StagedSession stagedSession = spy(session.mStagedSession); doReturn(packageName).when(stagedSession).getPackageName(); diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java index 639ae30c00b9..58489f398775 100644 --- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java +++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java @@ -81,7 +81,6 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -581,8 +580,6 @@ public class HintManagerServiceTest { HintManagerService service = createService(); IBinder token = new Binder(); int threadCount = 2; - - // session 1 has 2 non-isolated tids long sessionPtr1 = 111; CountDownLatch stopLatch1 = new CountDownLatch(1); int[] tids1 = createThreads(threadCount, stopLatch1); @@ -618,7 +615,6 @@ public class HintManagerServiceTest { IBinder token = new Binder(); int threadCount = 2; - // session 1 has 2 non-isolated tids long sessionPtr1 = 111; CountDownLatch stopLatch1 = new CountDownLatch(1); int[] tids1 = createThreads(threadCount, stopLatch1); @@ -630,67 +626,23 @@ public class HintManagerServiceTest { SessionTag.OTHER, new SessionConfig()); assertNotNull(session1); - // session 2 has 2 non-isolated tids and 2 isolated tids - long sessionPtr2 = 222; - CountDownLatch stopLatch2 = new CountDownLatch(1); - // negative value used for test only to avoid conflicting with any real thread that exists - int isoProc1 = -100; - int isoProc2 = 99999999; - when(mAmInternalMock.getIsolatedProcesses(eq(UID))).thenReturn(List.of(0)); - int[] tids2 = createThreads(threadCount, stopLatch2); - int[] tids2WithIsolated = Arrays.copyOf(tids2, tids2.length + 2); - tids2WithIsolated[threadCount] = isoProc1; - tids2WithIsolated[threadCount + 1] = isoProc2; - int[] expectedTids2 = Arrays.copyOf(tids2, tids2.length + 1); - expectedTids2[tids2.length] = isoProc1; - when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID), - eq(tids2WithIsolated), eq(DEFAULT_TARGET_DURATION), anyInt(), - any(SessionConfig.class))).thenReturn(sessionPtr2); - AppHintSession session2 = (AppHintSession) service.getBinderServiceInstance() - .createHintSessionWithConfig(token, tids2WithIsolated, - DEFAULT_TARGET_DURATION, SessionTag.OTHER, new SessionConfig()); - assertNotNull(session2); - - // trigger clean up through UID state change by making the process foreground->background - // this will remove the one unexpected isolated tid from session 2 - service.mUidObserver.onUidStateChanged(UID, - ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); - service.mUidObserver.onUidStateChanged(UID, - ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); - LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos( - CLEAN_UP_UID_DELAY_MILLIS)); - verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any()); - verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any()); - // the new TIDs pending list should be updated - assertArrayEquals(expectedTids2, session2.getTidsInternal()); - reset(mNativeWrapperMock); - - // this should resume and update the threads so those never-existed invalid isolated - // processes should be cleaned up - service.mUidObserver.onUidStateChanged(UID, - ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); - // wait for the async uid state change to trigger resume and setThreads - LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000)); - verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2), eq(expectedTids2)); - reset(mNativeWrapperMock); - // let all session 1 threads to exit and the cleanup should force pause the session 1 stopLatch1.countDown(); LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100)); service.mUidObserver.onUidStateChanged(UID, + ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); + service.mUidObserver.onUidStateChanged(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos( CLEAN_UP_UID_DELAY_MILLIS)); verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1)); verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any()); - verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any()); verifyAllHintsEnabled(session1, false); - verifyAllHintsEnabled(session2, false); service.mUidObserver.onUidStateChanged(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000)); + assertArrayEquals(tids1, session1.getTidsInternal()); verifyAllHintsEnabled(session1, false); - verifyAllHintsEnabled(session2, true); reset(mNativeWrapperMock); // in foreground, set new tids for session 1 then it should be resumed and all hints allowed @@ -704,8 +656,6 @@ public class HintManagerServiceTest { // let all session 1 and 2 non isolated threads to exit stopLatch1.countDown(); - stopLatch2.countDown(); - expectedTids2 = new int[]{isoProc1}; LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100)); service.mUidObserver.onUidStateChanged(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); @@ -713,14 +663,11 @@ public class HintManagerServiceTest { CLEAN_UP_UID_DELAY_MILLIS)); verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1)); verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any()); - verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any()); // in background, set threads for session 1 then it should not be force paused next time session1.setThreads(SESSION_TIDS_A); // the new TIDs pending list should be updated assertArrayEquals(SESSION_TIDS_A, session1.getTidsInternal()); - assertArrayEquals(expectedTids2, session2.getTidsInternal()); verifyAllHintsEnabled(session1, false); - verifyAllHintsEnabled(session2, false); reset(mNativeWrapperMock); service.mUidObserver.onUidStateChanged(UID, @@ -729,10 +676,7 @@ public class HintManagerServiceTest { CLEAN_UP_UID_DELAY_MILLIS)); verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr1), eq(SESSION_TIDS_A)); - verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2), - eq(expectedTids2)); verifyAllHintsEnabled(session1, true); - verifyAllHintsEnabled(session2, true); } private void verifyAllHintsEnabled(AppHintSession session, boolean verifyEnabled) { diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java index a1db18232c09..1c7fc63efd41 100644 --- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java @@ -54,6 +54,7 @@ import android.os.test.TestLooper; import android.provider.Settings; import android.testing.TestableContext; import android.util.IntArray; +import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.Display; import android.view.DisplayAddress; @@ -384,6 +385,32 @@ public class NotifierTest { } @Test + public void testOnGroupRemoved_perDisplayWakeByTouchEnabled() { + createNotifier(); + // GIVEN per-display wake by touch is enabled and one display group has been defined + when(mPowerManagerFlags.isPerDisplayWakeByTouchEnabled()).thenReturn(true); + final int groupId = 313; + final int displayId1 = 3113; + final int displayId2 = 4114; + final int[] displays = new int[]{displayId1, displayId2}; + when(mDisplayManagerInternal.getDisplayIds()).thenReturn(IntArray.wrap(displays)); + when(mDisplayManagerInternal.getDisplayIdsForGroup(groupId)).thenReturn(displays); + mNotifier.onGroupWakefulnessChangeStarted( + groupId, WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_TAP, /* eventTime= */ 1000); + final SparseBooleanArray expectedDisplayInteractivities = new SparseBooleanArray(); + expectedDisplayInteractivities.put(displayId1, true); + expectedDisplayInteractivities.put(displayId2, true); + verify(mInputManagerInternal).setDisplayInteractivities(expectedDisplayInteractivities); + + // WHEN display group is removed + when(mDisplayManagerInternal.getDisplayIdsByGroupsIds()).thenReturn(new SparseArray<>()); + mNotifier.onGroupRemoved(groupId); + + // THEN native input manager is informed that displays in that group no longer exist + verify(mInputManagerInternal).setDisplayInteractivities(new SparseBooleanArray()); + } + + @Test public void testOnWakeLockListener_RemoteException_NoRethrow() throws RemoteException { when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true); createNotifier(); diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp index d9e071f43e1b..d6ca10a23fb9 100644 --- a/services/tests/powerstatstests/Android.bp +++ b/services/tests/powerstatstests/Android.bp @@ -27,9 +27,6 @@ android_test { "servicestests-utils", "platform-test-annotations", "flag-junit", - "statsdprotolite", - "StatsdTestUtils", - "platformprotoslite", ], libs: [ @@ -77,10 +74,6 @@ android_ravenwood_test { "src/com/android/server/power/stats/format/*.java", "src/com/android/server/power/stats/processor/*.java", ], - // TODO(b/372292543): Enable this test. - exclude_srcs: [ - "src/com/android/server/power/stats/WakelockStatsFrameworkEventsTest.java", - ], java_resources: [ "res/xml/power_profile*.xml", ], diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java index cef3fddc6aba..03491bcfdf5a 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java @@ -25,7 +25,6 @@ import static org.mockito.Mockito.mock; import android.content.Context; import android.os.Process; import android.platform.test.annotations.DisableFlags; -import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.ravenwood.RavenwoodConfig; import android.platform.test.ravenwood.RavenwoodConfig.Config; @@ -76,7 +75,6 @@ public class WakelockPowerStatsCollectorTest { @Test @DisableFlags(Flags.FLAG_FRAMEWORK_WAKELOCK_INFO) - @DisabledOnRavenwood(reason = "b/372292543 temporary disable") public void collectStats() { PowerStatsCollector powerStatsCollector = mBatteryStats.getPowerStatsCollector( POWER_COMPONENT_WAKELOCK); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockStatsFrameworkEventsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockStatsFrameworkEventsTest.java index cb644dbe8c96..1fe3f58a9dcb 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockStatsFrameworkEventsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockStatsFrameworkEventsTest.java @@ -19,41 +19,25 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.os.WakeLockLevelEnum; -import android.util.StatsEvent; -import android.util.StatsEventTestUtils; +import android.os.nano.OsProtoEnums; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.os.AtomsProto; -import com.android.os.framework.FrameworkExtensionAtoms; -import com.android.os.framework.FrameworkExtensionAtoms.FrameworkWakelockInfo; - -import com.google.protobuf.CodedInputStream; -import com.google.protobuf.CodedOutputStream; -import com.google.protobuf.ExtensionRegistryLite; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.util.ArrayList; -import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest public class WakelockStatsFrameworkEventsTest { private WakelockStatsFrameworkEvents mEvents; - private ExtensionRegistryLite mRegistry; @Before public void setup() { mEvents = new WakelockStatsFrameworkEvents(); - mRegistry = ExtensionRegistryLite.newInstance(); - FrameworkExtensionAtoms.registerAllExtensions(mRegistry); } private static final int UID_1 = 1; @@ -62,8 +46,8 @@ public class WakelockStatsFrameworkEventsTest { private static final String TAG_1 = "TAG1"; private static final String TAG_2 = "TAG2"; - private static final WakeLockLevelEnum WAKELOCK_TYPE_1 = WakeLockLevelEnum.PARTIAL_WAKE_LOCK; - private static final WakeLockLevelEnum WAKELOCK_TYPE_2 = WakeLockLevelEnum.DOZE_WAKE_LOCK; + private static final int WAKELOCK_TYPE_1 = OsProtoEnums.PARTIAL_WAKE_LOCK; + private static final int WAKELOCK_TYPE_2 = OsProtoEnums.DOZE_WAKE_LOCK; private static final long TS_1 = 1000; private static final long TS_2 = 2000; @@ -71,19 +55,37 @@ public class WakelockStatsFrameworkEventsTest { private static final long TS_4 = 4000; private static final long TS_5 = 5000; + // Mirrors com.android.os.framework.FrameworkWakelockInfo proto. + private static class WakelockInfo { + public int uid; + public String tag; + public int type; + public long uptimeMillis; + public long completedCount; + + WakelockInfo(int uid, String tag, int type, long uptimeMillis, long completedCount) { + this.uid = uid; + this.tag = tag; + this.type = type; + this.uptimeMillis = uptimeMillis; + this.completedCount = completedCount; + } + } + // Assumes that mEvents is empty. + @SuppressWarnings("GuardedBy") private void makeMetricsAlmostOverflow() throws Exception { for (int i = 0; i < mEvents.SUMMARY_THRESHOLD - 1; i++) { String tag = "forceOverflow" + i; - mEvents.noteStartWakeLock(UID_1, tag, WAKELOCK_TYPE_1.getNumber(), TS_1); - mEvents.noteStopWakeLock(UID_1, tag, WAKELOCK_TYPE_1.getNumber(), TS_2); + mEvents.noteStartWakeLock(UID_1, tag, WAKELOCK_TYPE_1, TS_1); + mEvents.noteStopWakeLock(UID_1, tag, WAKELOCK_TYPE_1, TS_2); } assertFalse("not overflow", mEvents.inOverflow()); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_4); - FrameworkWakelockInfo notOverflowInfo = + ArrayList<WakelockInfo> info = pullResults(TS_4); + WakelockInfo notOverflowInfo = info.stream() - .filter(i -> i.getAttributionTag().equals(mEvents.OVERFLOW_TAG)) + .filter(i -> i.tag.equals(mEvents.OVERFLOW_TAG)) .findFirst() .orElse(null); @@ -91,38 +93,36 @@ public class WakelockStatsFrameworkEventsTest { // Add one more to hit an overflow state. String lastTag = "forceOverflowLast"; - mEvents.noteStartWakeLock(UID_1, lastTag, WAKELOCK_TYPE_2.getNumber(), TS_1); - mEvents.noteStopWakeLock(UID_1, lastTag, WAKELOCK_TYPE_2.getNumber(), TS_2); + mEvents.noteStartWakeLock(UID_1, lastTag, WAKELOCK_TYPE_2, TS_1); + mEvents.noteStopWakeLock(UID_1, lastTag, WAKELOCK_TYPE_2, TS_2); assertTrue("overflow", mEvents.inOverflow()); info = pullResults(TS_4); - FrameworkWakelockInfo tag1Info = - info.stream() - .filter(i -> i.getAttributionTag().equals(lastTag)) - .findFirst() - .orElse(null); + WakelockInfo tag1Info = + info.stream().filter(i -> i.tag.equals(lastTag)).findFirst().orElse(null); assertTrue("lastTag found", tag1Info != null); - assertEquals("uid", UID_1, tag1Info.getAttributionUid()); - assertEquals("tag", lastTag, tag1Info.getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_2, tag1Info.getType()); - assertEquals("duration", TS_2 - TS_1, tag1Info.getUptimeMillis()); - assertEquals("count", 1, tag1Info.getCompletedCount()); + assertEquals("uid", UID_1, tag1Info.uid); + assertEquals("tag", lastTag, tag1Info.tag); + assertEquals("type", WAKELOCK_TYPE_2, tag1Info.type); + assertEquals("duration", TS_2 - TS_1, tag1Info.uptimeMillis); + assertEquals("count", 1, tag1Info.completedCount); } // Assumes that mEvents is empty. + @SuppressWarnings("GuardedBy") private void makeMetricsAlmostHardCap() throws Exception { for (int i = 0; i < mEvents.MAX_WAKELOCK_DIMENSIONS - 1; i++) { - mEvents.noteStartWakeLock(i /* uid */, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_1); - mEvents.noteStopWakeLock(i /* uid */, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_2); + mEvents.noteStartWakeLock(i /* uid */, TAG_1, WAKELOCK_TYPE_1, TS_1); + mEvents.noteStopWakeLock(i /* uid */, TAG_1, WAKELOCK_TYPE_1, TS_2); } assertFalse("not hard capped", mEvents.inHardCap()); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_4); - FrameworkWakelockInfo notOverflowInfo = + ArrayList<WakelockInfo> info = pullResults(TS_4); + WakelockInfo notOverflowInfo = info.stream() - .filter(i -> i.getAttributionTag().equals(mEvents.HARD_CAP_TAG)) + .filter(i -> i.tag.equals(mEvents.HARD_CAP_TAG)) .findFirst() .orElse(null); @@ -130,98 +130,86 @@ public class WakelockStatsFrameworkEventsTest { // Add one more to hit an hardcap state. int hardCapUid = mEvents.MAX_WAKELOCK_DIMENSIONS; - mEvents.noteStartWakeLock(hardCapUid, TAG_2, WAKELOCK_TYPE_2.getNumber(), TS_1); - mEvents.noteStopWakeLock(hardCapUid, TAG_2, WAKELOCK_TYPE_2.getNumber(), TS_2); + mEvents.noteStartWakeLock(hardCapUid, TAG_2, WAKELOCK_TYPE_2, TS_1); + mEvents.noteStopWakeLock(hardCapUid, TAG_2, WAKELOCK_TYPE_2, TS_2); assertTrue("hard capped", mEvents.inHardCap()); info = pullResults(TS_4); - FrameworkWakelockInfo tag2Info = - info.stream() - .filter(i -> i.getAttributionUid() == hardCapUid) - .findFirst() - .orElse(null); + WakelockInfo tag2Info = + info.stream().filter(i -> i.uid == hardCapUid).findFirst().orElse(null); assertTrue("hardCapUid found", tag2Info != null); - assertEquals("uid", hardCapUid, tag2Info.getAttributionUid()); - assertEquals("tag", mEvents.OVERFLOW_TAG, tag2Info.getAttributionTag()); - assertEquals( - "type", WakeLockLevelEnum.forNumber(mEvents.OVERFLOW_LEVEL), tag2Info.getType()); - assertEquals("duration", TS_2 - TS_1, tag2Info.getUptimeMillis()); - assertEquals("count", 1, tag2Info.getCompletedCount()); + assertEquals("uid", hardCapUid, tag2Info.uid); + assertEquals("tag", mEvents.OVERFLOW_TAG, tag2Info.tag); + assertEquals("type", mEvents.OVERFLOW_LEVEL, tag2Info.type); + assertEquals("duration", TS_2 - TS_1, tag2Info.uptimeMillis); + assertEquals("count", 1, tag2Info.completedCount); } - private ArrayList<FrameworkWakelockInfo> pullResults(long timestamp) throws Exception { - ArrayList<FrameworkWakelockInfo> result = new ArrayList<>(); - List<StatsEvent> events = mEvents.pullFrameworkWakelockInfoAtoms(timestamp); - - for (StatsEvent e : events) { - // The returned atom does not have external extensions registered. - // So we serialize and then deserialize with extensions registered. - AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(e); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - CodedOutputStream codedos = CodedOutputStream.newInstance(outputStream); - atom.writeTo(codedos); - codedos.flush(); - - ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); - CodedInputStream codedis = CodedInputStream.newInstance(inputStream); - AtomsProto.Atom atomWithExtensions = AtomsProto.Atom.parseFrom(codedis, mRegistry); - - assertTrue( - atomWithExtensions.hasExtension(FrameworkExtensionAtoms.frameworkWakelockInfo)); - FrameworkWakelockInfo info = - atomWithExtensions.getExtension(FrameworkExtensionAtoms.frameworkWakelockInfo); - result.add(info); - } - - return result; + private ArrayList<WakelockInfo> pullResults(long timestamp) { + ArrayList<WakelockInfo> results = new ArrayList<>(); + WakelockStatsFrameworkEvents.EventLogger logger = + new WakelockStatsFrameworkEvents.EventLogger() { + public void logResult( + int uid, + String tag, + int wakeLockLevel, + long uptimeMillis, + long completedCount) { + WakelockInfo info = + new WakelockInfo( + uid, tag, wakeLockLevel, uptimeMillis, completedCount); + results.add(info); + } + }; + mEvents.pullFrameworkWakelockInfoAtoms(timestamp, logger); + return results; } @Test - public void singleWakelock() throws Exception { - mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_1); - mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_2); + public void singleWakelock() { + mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); + mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_2); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_3); + ArrayList<WakelockInfo> info = pullResults(TS_3); assertEquals("size", 1, info.size()); - assertEquals("uid", UID_1, info.get(0).getAttributionUid()); - assertEquals("tag", TAG_1, info.get(0).getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_1, info.get(0).getType()); - assertEquals("duration", TS_2 - TS_1, info.get(0).getUptimeMillis()); - assertEquals("count", 1, info.get(0).getCompletedCount()); + assertEquals("uid", UID_1, info.get(0).uid); + assertEquals("tag", TAG_1, info.get(0).tag); + assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type); + assertEquals("duration", TS_2 - TS_1, info.get(0).uptimeMillis); + assertEquals("count", 1, info.get(0).completedCount); } @Test public void wakelockOpen() throws Exception { - mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_1); + mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_3); + ArrayList<WakelockInfo> info = pullResults(TS_3); assertEquals("size", 1, info.size()); - assertEquals("uid", UID_1, info.get(0).getAttributionUid()); - assertEquals("tag", TAG_1, info.get(0).getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_1, info.get(0).getType()); - assertEquals("duration", TS_3 - TS_1, info.get(0).getUptimeMillis()); - assertEquals("count", 0, info.get(0).getCompletedCount()); + assertEquals("uid", UID_1, info.get(0).uid); + assertEquals("tag", TAG_1, info.get(0).tag); + assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type); + assertEquals("duration", TS_3 - TS_1, info.get(0).uptimeMillis); + assertEquals("count", 0, info.get(0).completedCount); } @Test public void wakelockOpenOverlap() throws Exception { - mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_1); - mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_2); - mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_3); + mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); + mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_2); + mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_3); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_4); + ArrayList<WakelockInfo> info = pullResults(TS_4); assertEquals("size", 1, info.size()); - assertEquals("uid", UID_1, info.get(0).getAttributionUid()); - assertEquals("tag", TAG_1, info.get(0).getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_1, info.get(0).getType()); - assertEquals("duration", TS_4 - TS_1, info.get(0).getUptimeMillis()); - assertEquals("count", 0, info.get(0).getCompletedCount()); + assertEquals("uid", UID_1, info.get(0).uid); + assertEquals("tag", TAG_1, info.get(0).tag); + assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type); + assertEquals("duration", TS_4 - TS_1, info.get(0).uptimeMillis); + assertEquals("count", 0, info.get(0).completedCount); } @Test @@ -229,23 +217,20 @@ public class WakelockStatsFrameworkEventsTest { makeMetricsAlmostOverflow(); // This one gets tagged as an overflow. - mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2.getNumber(), TS_1); - mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2.getNumber(), TS_2); + mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1); + mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_2); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_4); - FrameworkWakelockInfo overflowInfo = + ArrayList<WakelockInfo> info = pullResults(TS_4); + WakelockInfo overflowInfo = info.stream() - .filter(i -> i.getAttributionTag().equals(mEvents.OVERFLOW_TAG)) + .filter(i -> i.tag.equals(mEvents.OVERFLOW_TAG)) .findFirst() .orElse(null); - assertEquals("uid", UID_1, overflowInfo.getAttributionUid()); - assertEquals( - "type", - WakeLockLevelEnum.forNumber(mEvents.OVERFLOW_LEVEL), - overflowInfo.getType()); - assertEquals("duration", TS_2 - TS_1, overflowInfo.getUptimeMillis()); - assertEquals("count", 1, overflowInfo.getCompletedCount()); + assertEquals("uid", UID_1, overflowInfo.uid); + assertEquals("type", mEvents.OVERFLOW_LEVEL, overflowInfo.type); + assertEquals("duration", TS_2 - TS_1, overflowInfo.uptimeMillis); + assertEquals("count", 1, overflowInfo.completedCount); } @Test @@ -253,22 +238,19 @@ public class WakelockStatsFrameworkEventsTest { makeMetricsAlmostOverflow(); // This is the open wakelock that overflows. - mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2.getNumber(), TS_1); + mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_4); - FrameworkWakelockInfo overflowInfo = + ArrayList<WakelockInfo> info = pullResults(TS_4); + WakelockInfo overflowInfo = info.stream() - .filter(i -> i.getAttributionTag().equals(mEvents.OVERFLOW_TAG)) + .filter(i -> i.tag.equals(mEvents.OVERFLOW_TAG)) .findFirst() .orElse(null); - assertEquals("uid", UID_1, overflowInfo.getAttributionUid()); - assertEquals( - "type", - WakeLockLevelEnum.forNumber(mEvents.OVERFLOW_LEVEL), - overflowInfo.getType()); - assertEquals("duration", (TS_4 - TS_1), overflowInfo.getUptimeMillis()); - assertEquals("count", 0, overflowInfo.getCompletedCount()); + assertEquals("uid", UID_1, overflowInfo.uid); + assertEquals("type", mEvents.OVERFLOW_LEVEL, overflowInfo.type); + assertEquals("duration", (TS_4 - TS_1), overflowInfo.uptimeMillis); + assertEquals("count", 0, overflowInfo.completedCount); } @Test @@ -276,23 +258,20 @@ public class WakelockStatsFrameworkEventsTest { makeMetricsAlmostHardCap(); // This one gets tagged as a hard cap. - mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2.getNumber(), TS_1); - mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2.getNumber(), TS_2); + mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1); + mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_2); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_4); - FrameworkWakelockInfo hardCapInfo = + ArrayList<WakelockInfo> info = pullResults(TS_4); + WakelockInfo hardCapInfo = info.stream() - .filter(i -> i.getAttributionTag().equals(mEvents.HARD_CAP_TAG)) + .filter(i -> i.tag.equals(mEvents.HARD_CAP_TAG)) .findFirst() .orElse(null); - assertEquals("uid", mEvents.HARD_CAP_UID, hardCapInfo.getAttributionUid()); - assertEquals( - "type", - WakeLockLevelEnum.forNumber(mEvents.OVERFLOW_LEVEL), - hardCapInfo.getType()); - assertEquals("duration", TS_2 - TS_1, hardCapInfo.getUptimeMillis()); - assertEquals("count", 1, hardCapInfo.getCompletedCount()); + assertEquals("uid", mEvents.HARD_CAP_UID, hardCapInfo.uid); + assertEquals("type", mEvents.OVERFLOW_LEVEL, hardCapInfo.type); + assertEquals("duration", TS_2 - TS_1, hardCapInfo.uptimeMillis); + assertEquals("count", 1, hardCapInfo.completedCount); } @Test @@ -300,134 +279,123 @@ public class WakelockStatsFrameworkEventsTest { makeMetricsAlmostHardCap(); // This is the open wakelock that overflows. - mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2.getNumber(), TS_1); + mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_4); - FrameworkWakelockInfo hardCapInfo = + ArrayList<WakelockInfo> info = pullResults(TS_4); + WakelockInfo hardCapInfo = info.stream() - .filter(i -> i.getAttributionTag().equals(mEvents.HARD_CAP_TAG)) + .filter(i -> i.tag.equals(mEvents.HARD_CAP_TAG)) .findFirst() .orElse(null); - assertEquals("uid", mEvents.HARD_CAP_UID, hardCapInfo.getAttributionUid()); - assertEquals( - "type", - WakeLockLevelEnum.forNumber(mEvents.OVERFLOW_LEVEL), - hardCapInfo.getType()); - assertEquals("duration", (TS_4 - TS_1), hardCapInfo.getUptimeMillis()); - assertEquals("count", 0, hardCapInfo.getCompletedCount()); + assertEquals("uid", mEvents.HARD_CAP_UID, hardCapInfo.uid); + assertEquals("type", mEvents.OVERFLOW_LEVEL, hardCapInfo.type); + assertEquals("duration", (TS_4 - TS_1), hardCapInfo.uptimeMillis); + assertEquals("count", 0, hardCapInfo.completedCount); } @Test public void overlappingWakelocks() throws Exception { - mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_1); - mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_2); - mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_3); - mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_4); + mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); + mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_2); + mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_3); + mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_5); + ArrayList<WakelockInfo> info = pullResults(TS_5); assertEquals("size", 1, info.size()); - assertEquals("uid", UID_1, info.get(0).getAttributionUid()); - assertEquals("tag", TAG_1, info.get(0).getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_1, info.get(0).getType()); - assertEquals("duration", TS_4 - TS_1, info.get(0).getUptimeMillis()); - assertEquals("count", 1, info.get(0).getCompletedCount()); + assertEquals("uid", UID_1, info.get(0).uid); + assertEquals("tag", TAG_1, info.get(0).tag); + assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type); + assertEquals("duration", TS_4 - TS_1, info.get(0).uptimeMillis); + assertEquals("count", 1, info.get(0).completedCount); } @Test public void diffUid() throws Exception { - mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_1); - mEvents.noteStartWakeLock(UID_2, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_2); - mEvents.noteStopWakeLock(UID_2, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_3); - mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_4); + mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); + mEvents.noteStartWakeLock(UID_2, TAG_1, WAKELOCK_TYPE_1, TS_2); + mEvents.noteStopWakeLock(UID_2, TAG_1, WAKELOCK_TYPE_1, TS_3); + mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_5); + ArrayList<WakelockInfo> info = pullResults(TS_5); assertEquals("size", 2, info.size()); - FrameworkWakelockInfo uid1Info = - info.stream().filter(i -> i.getAttributionUid() == UID_1).findFirst().orElse(null); + WakelockInfo uid1Info = info.stream().filter(i -> i.uid == UID_1).findFirst().orElse(null); assertTrue("UID_1 found", uid1Info != null); - assertEquals("uid", UID_1, uid1Info.getAttributionUid()); - assertEquals("tag", TAG_1, uid1Info.getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_1, uid1Info.getType()); - assertEquals("duration", TS_4 - TS_1, uid1Info.getUptimeMillis()); - assertEquals("count", 1, uid1Info.getCompletedCount()); - - FrameworkWakelockInfo uid2Info = - info.stream().filter(i -> i.getAttributionUid() == UID_2).findFirst().orElse(null); + assertEquals("uid", UID_1, uid1Info.uid); + assertEquals("tag", TAG_1, uid1Info.tag); + assertEquals("type", WAKELOCK_TYPE_1, uid1Info.type); + assertEquals("duration", TS_4 - TS_1, uid1Info.uptimeMillis); + assertEquals("count", 1, uid1Info.completedCount); + + WakelockInfo uid2Info = info.stream().filter(i -> i.uid == UID_2).findFirst().orElse(null); assertTrue("UID_2 found", uid2Info != null); - assertEquals("uid", UID_2, uid2Info.getAttributionUid()); - assertEquals("tag", TAG_1, uid2Info.getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_1, uid2Info.getType()); - assertEquals("duration", TS_3 - TS_2, uid2Info.getUptimeMillis()); - assertEquals("count", 1, uid2Info.getCompletedCount()); + assertEquals("uid", UID_2, uid2Info.uid); + assertEquals("tag", TAG_1, uid2Info.tag); + assertEquals("type", WAKELOCK_TYPE_1, uid2Info.type); + assertEquals("duration", TS_3 - TS_2, uid2Info.uptimeMillis); + assertEquals("count", 1, uid2Info.completedCount); } @Test public void diffTag() throws Exception { - mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_1); - mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_1.getNumber(), TS_2); - mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_1.getNumber(), TS_3); - mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_4); + mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); + mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_1, TS_2); + mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_1, TS_3); + mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_5); + ArrayList<WakelockInfo> info = pullResults(TS_5); assertEquals("size", 2, info.size()); - FrameworkWakelockInfo uid1Info = - info.stream() - .filter(i -> i.getAttributionTag().equals(TAG_1)) - .findFirst() - .orElse(null); + WakelockInfo uid1Info = + info.stream().filter(i -> i.tag.equals(TAG_1)).findFirst().orElse(null); assertTrue("TAG_1 found", uid1Info != null); - assertEquals("uid", UID_1, uid1Info.getAttributionUid()); - assertEquals("tag", TAG_1, uid1Info.getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_1, uid1Info.getType()); - assertEquals("duration", TS_4 - TS_1, uid1Info.getUptimeMillis()); - assertEquals("count", 1, uid1Info.getCompletedCount()); - - FrameworkWakelockInfo uid2Info = - info.stream() - .filter(i -> i.getAttributionTag().equals(TAG_2)) - .findFirst() - .orElse(null); + assertEquals("uid", UID_1, uid1Info.uid); + assertEquals("tag", TAG_1, uid1Info.tag); + assertEquals("type", WAKELOCK_TYPE_1, uid1Info.type); + assertEquals("duration", TS_4 - TS_1, uid1Info.uptimeMillis); + assertEquals("count", 1, uid1Info.completedCount); + + WakelockInfo uid2Info = + info.stream().filter(i -> i.tag.equals(TAG_2)).findFirst().orElse(null); assertTrue("TAG_2 found", uid2Info != null); - assertEquals("uid", UID_1, uid2Info.getAttributionUid()); - assertEquals("tag", TAG_2, uid2Info.getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_1, uid2Info.getType()); - assertEquals("duration", TS_3 - TS_2, uid2Info.getUptimeMillis()); - assertEquals("count", 1, uid2Info.getCompletedCount()); + assertEquals("uid", UID_1, uid2Info.uid); + assertEquals("tag", TAG_2, uid2Info.tag); + assertEquals("type", WAKELOCK_TYPE_1, uid2Info.type); + assertEquals("duration", TS_3 - TS_2, uid2Info.uptimeMillis); + assertEquals("count", 1, uid2Info.completedCount); } @Test public void diffType() throws Exception { - mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_1); - mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_2.getNumber(), TS_2); - mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_2.getNumber(), TS_3); - mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1.getNumber(), TS_4); + mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1); + mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_2, TS_2); + mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_2, TS_3); + mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4); - ArrayList<FrameworkWakelockInfo> info = pullResults(TS_5); + ArrayList<WakelockInfo> info = pullResults(TS_5); assertEquals("size", 2, info.size()); - FrameworkWakelockInfo uid1Info = - info.stream().filter(i -> i.getType() == WAKELOCK_TYPE_1).findFirst().orElse(null); + WakelockInfo uid1Info = + info.stream().filter(i -> i.type == WAKELOCK_TYPE_1).findFirst().orElse(null); assertTrue("WAKELOCK_TYPE_1 found", uid1Info != null); - assertEquals("uid", UID_1, uid1Info.getAttributionUid()); - assertEquals("tag", TAG_1, uid1Info.getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_1, uid1Info.getType()); - assertEquals("duration", TS_4 - TS_1, uid1Info.getUptimeMillis()); - assertEquals("count", 1, uid1Info.getCompletedCount()); - - FrameworkWakelockInfo uid2Info = - info.stream().filter(i -> i.getType() == WAKELOCK_TYPE_2).findFirst().orElse(null); + assertEquals("uid", UID_1, uid1Info.uid); + assertEquals("tag", TAG_1, uid1Info.tag); + assertEquals("type", WAKELOCK_TYPE_1, uid1Info.type); + assertEquals("duration", TS_4 - TS_1, uid1Info.uptimeMillis); + assertEquals("count", 1, uid1Info.completedCount); + + WakelockInfo uid2Info = + info.stream().filter(i -> i.type == WAKELOCK_TYPE_2).findFirst().orElse(null); assertTrue("WAKELOCK_TYPE_2 found", uid2Info != null); - assertEquals("uid", UID_1, uid2Info.getAttributionUid()); - assertEquals("tag", TAG_1, uid2Info.getAttributionTag()); - assertEquals("type", WAKELOCK_TYPE_2, uid2Info.getType()); - assertEquals("duration", TS_3 - TS_2, uid2Info.getUptimeMillis()); - assertEquals("count", 1, uid2Info.getCompletedCount()); + assertEquals("uid", UID_1, uid2Info.uid); + assertEquals("tag", TAG_1, uid2Info.tag); + assertEquals("type", WAKELOCK_TYPE_2, uid2Info.type); + assertEquals("duration", TS_3 - TS_2, uid2Info.uptimeMillis); + assertEquals("count", 1, uid2Info.completedCount); } } diff --git a/services/tests/security/forensic/Android.bp b/services/tests/security/forensic/Android.bp new file mode 100644 index 000000000000..adc49040a004 --- /dev/null +++ b/services/tests/security/forensic/Android.bp @@ -0,0 +1,41 @@ +package { + default_team: "trendy_team_platform_security", + // 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_test { + name: "ForensicServiceTests", + srcs: [ + "src/**/*.java", + ], + + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.runner", + "compatibility-device-util-axt", + "frameworks-base-testutils", + "junit", + "platform-test-annotations", + "services.core", + "truth", + ], + + platform_apis: true, + + test_suites: [ + "device-tests", + "automotive-tests", + ], + + certificate: "platform", + dxflags: ["--multi-dex"], + optimize: { + enabled: false, + }, +} diff --git a/services/tests/security/forensic/AndroidManifest.xml b/services/tests/security/forensic/AndroidManifest.xml new file mode 100644 index 000000000000..40feb19aecba --- /dev/null +++ b/services/tests/security/forensic/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.security.forensic.tests"> + + <application android:testOnly="true"> + <uses-library android:name="android.test.runner"/> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.security.forensic.tests" + android:label="Frameworks Forensic Services Tests"/> +</manifest> diff --git a/services/tests/security/forensic/AndroidTest.xml b/services/tests/security/forensic/AndroidTest.xml new file mode 100644 index 000000000000..bbe2e9c303ce --- /dev/null +++ b/services/tests/security/forensic/AndroidTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs Frameworks Forensic Service tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="ForensicServiceTests.apk"/> + <option name="install-arg" value="-t" /> + </target_preparer> + + <option name="test-tag" value="ForensicServiceTests" /> + <test class="com.android.tradefed.testtype.InstrumentationTest" > + <option name="package" value="com.android.server.security.forensic.tests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/services/tests/security/forensic/TEST_MAPPING b/services/tests/security/forensic/TEST_MAPPING new file mode 100644 index 000000000000..bd8b2ab7c41f --- /dev/null +++ b/services/tests/security/forensic/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "ForensicServiceTests" + } + ] +} diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java new file mode 100644 index 000000000000..7aa2e0f609a7 --- /dev/null +++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.forensic; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Looper; +import android.os.RemoteException; +import android.os.test.TestLooper; +import android.security.forensic.IForensicServiceCommandCallback; +import android.security.forensic.IForensicServiceStateCallback; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class ForensicServiceTest { + private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN; + private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE; + private static final int STATE_VISIBLE = IForensicServiceStateCallback.State.VISIBLE; + private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED; + + private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN; + private static final int ERROR_PERMISSION_DENIED = + IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED; + private static final int ERROR_INVALID_STATE_TRANSITION = + IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION; + private static final int ERROR_BACKUP_TRANSPORT_UNAVAILABLE = + IForensicServiceCommandCallback.ErrorCode.BACKUP_TRANSPORT_UNAVAILABLE; + private static final int ERROR_DATA_SOURCE_UNAVAILABLE = + IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE; + + @Mock + private Context mContextSpy; + + private ForensicService mForensicService; + private TestLooper mTestLooper; + private Looper mLooper; + + @SuppressLint("VisibleForTests") + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mTestLooper = new TestLooper(); + mLooper = mTestLooper.getLooper(); + mForensicService = new ForensicService(new MockInjector(mContextSpy)); + mForensicService.onStart(); + } + + @Test + public void testMonitorState_Invisible() throws RemoteException { + StateCallback scb = new StateCallback(); + assertEquals(STATE_UNKNOWN, scb.mState); + mForensicService.getBinderService().monitorState(scb); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb.mState); + } + + @Test + public void testMonitorState_Invisible_TwoMonitors() throws RemoteException { + StateCallback scb1 = new StateCallback(); + assertEquals(STATE_UNKNOWN, scb1.mState); + mForensicService.getBinderService().monitorState(scb1); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + + StateCallback scb2 = new StateCallback(); + assertEquals(STATE_UNKNOWN, scb2.mState); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb2.mState); + } + + @Test + public void testMakeVisible_FromInvisible() throws RemoteException { + StateCallback scb = new StateCallback(); + assertEquals(STATE_UNKNOWN, scb.mState); + mForensicService.getBinderService().monitorState(scb); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().makeVisible(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb.mState); + assertNull(ccb.mErrorCode); + } + + @Test + public void testMakeVisible_FromInvisible_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_INVISIBLE); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().makeVisible(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb1.mState); + assertEquals(STATE_VISIBLE, scb2.mState); + assertNull(ccb.mErrorCode); + } + + @Test + public void testMakeVisible_FromVisible_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_VISIBLE); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb1.mState); + assertEquals(STATE_VISIBLE, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().makeVisible(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb1.mState); + assertEquals(STATE_VISIBLE, scb2.mState); + assertNull(ccb.mErrorCode); + } + + @Test + public void testMakeVisible_FromEnabled_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_ENABLED); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_ENABLED, scb1.mState); + assertEquals(STATE_ENABLED, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().makeVisible(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_ENABLED, scb1.mState); + assertEquals(STATE_ENABLED, scb2.mState); + assertNotNull(ccb.mErrorCode); + assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue()); + } + + @Test + public void testMakeInvisible_FromInvisible_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_INVISIBLE); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().makeInvisible(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + assertNull(ccb.mErrorCode); + } + + @Test + public void testMakeInvisible_FromVisible_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_VISIBLE); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb1.mState); + assertEquals(STATE_VISIBLE, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().makeInvisible(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + assertNull(ccb.mErrorCode); + } + + @Test + public void testMakeInvisible_FromEnabled_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_ENABLED); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_ENABLED, scb1.mState); + assertEquals(STATE_ENABLED, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().makeInvisible(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + assertNull(ccb.mErrorCode); + } + + + @Test + public void testEnable_FromInvisible_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_INVISIBLE); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().enable(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + assertNotNull(ccb.mErrorCode); + assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue()); + } + + @Test + public void testEnable_FromVisible_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_VISIBLE); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb1.mState); + assertEquals(STATE_VISIBLE, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().enable(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_ENABLED, scb1.mState); + assertEquals(STATE_ENABLED, scb2.mState); + assertNull(ccb.mErrorCode); + } + + @Test + public void testEnable_FromEnabled_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_ENABLED); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_ENABLED, scb1.mState); + assertEquals(STATE_ENABLED, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().enable(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_ENABLED, scb1.mState); + assertEquals(STATE_ENABLED, scb2.mState); + assertNull(ccb.mErrorCode); + } + + @Test + public void testDisable_FromInvisible_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_INVISIBLE); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().disable(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + assertNotNull(ccb.mErrorCode); + assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue()); + } + + @Test + public void testDisable_FromVisible_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_VISIBLE); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb1.mState); + assertEquals(STATE_VISIBLE, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().disable(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb1.mState); + assertEquals(STATE_VISIBLE, scb2.mState); + assertNull(ccb.mErrorCode); + } + + @Test + public void testDisable_FromEnabled_TwoMonitors() throws RemoteException { + mForensicService.setState(STATE_ENABLED); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_ENABLED, scb1.mState); + assertEquals(STATE_ENABLED, scb2.mState); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().disable(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb1.mState); + assertEquals(STATE_VISIBLE, scb2.mState); + assertNull(ccb.mErrorCode); + } + + private class MockInjector implements ForensicService.Injector { + private final Context mContext; + + MockInjector(Context context) { + mContext = context; + } + + @Override + public Context getContext() { + return mContext; + } + + + @Override + public Looper getLooper() { + return mLooper; + } + + } + + private static class StateCallback extends IForensicServiceStateCallback.Stub { + int mState = STATE_UNKNOWN; + + @Override + public void onStateChange(int state) throws RemoteException { + mState = state; + } + } + + private static class CommandCallback extends IForensicServiceCommandCallback.Stub { + Integer mErrorCode = null; + + public void reset() { + mErrorCode = null; + } + + @Override + public void onSuccess() throws RemoteException { + + } + + @Override + public void onFailure(int errorCode) throws RemoteException { + mErrorCode = errorCode; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index b745e6a7d4a5..e5831b326de5 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -1417,7 +1417,7 @@ public class FullScreenMagnificationGestureHandlerTest { } @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseMoveEventsDoNotMoveMagnifierViewport() { runMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE); } @@ -1471,55 +1471,55 @@ public class FullScreenMagnificationGestureHandlerTest { } @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseHoverMoveEventsDoNotMoveMagnifierViewport() { runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE); } @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testStylusHoverMoveEventsDoNotMoveMagnifierViewport() { runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_STYLUS); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseHoverMoveEventsMoveMagnifierViewport() { runHoverMovesViewportTest(InputDevice.SOURCE_MOUSE); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testStylusHoverMoveEventsMoveMagnifierViewport() { runHoverMovesViewportTest(InputDevice.SOURCE_STYLUS); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseDownEventsDoNotMoveMagnifierViewport() { runDownDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testStylusDownEventsDoNotMoveMagnifierViewport() { runDownDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseUpEventsDoNotMoveMagnifierViewport() { runUpDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testStylusUpEventsDoNotMoveMagnifierViewport() { runUpDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS); } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void testMouseMoveEventsMoveMagnifierViewport() { final EventCaptor eventCaptor = new EventCaptor(); mMgh.setNext(eventCaptor); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java index d80a1f056e94..45c157d1c1a0 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java @@ -93,7 +93,7 @@ public class MagnificationGestureHandlerTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void onMotionEvent_isFromMouse_handleMouseOrStylusEvent() { final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0); mouseEvent.setSource(InputDevice.SOURCE_MOUSE); @@ -108,7 +108,7 @@ public class MagnificationGestureHandlerTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void onMotionEvent_isFromStylus_handleMouseOrStylusEvent() { final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0); stylusEvent.setSource(InputDevice.SOURCE_STYLUS); @@ -123,7 +123,7 @@ public class MagnificationGestureHandlerTest { } @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void onMotionEvent_isFromMouse_handleMouseOrStylusEventNotCalled() { final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0); mouseEvent.setSource(InputDevice.SOURCE_MOUSE); @@ -138,7 +138,7 @@ public class MagnificationGestureHandlerTest { } @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE) + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX) public void onMotionEvent_isFromStylus_handleMouseOrStylusEventNotCalled() { final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0); stylusEvent.setSource(InputDevice.SOURCE_STYLUS); diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index d1f6c2f9f1f0..9c6412b81b34 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -67,10 +67,8 @@ import android.provider.Settings; import androidx.test.InstrumentationRegistry; import com.android.internal.R; -import com.android.internal.pm.parsing.PackageParser2; import com.android.server.compat.PlatformCompat; import com.android.server.integrity.model.IntegrityCheckResult; -import com.android.server.pm.parsing.TestPackageParser2; import com.android.server.testutils.TestUtils; import org.junit.After; @@ -140,8 +138,6 @@ public class AppIntegrityManagerServiceImplTest { @Mock IntegrityFileManager mIntegrityFileManager; @Mock Handler mHandler; - private Supplier<PackageParser2> mParserSupplier = TestPackageParser2::new; - private final Context mRealContext = InstrumentationRegistry.getTargetContext(); private PackageManager mSpyPackageManager; @@ -173,7 +169,6 @@ public class AppIntegrityManagerServiceImplTest { new AppIntegrityManagerServiceImpl( mMockContext, mPackageManagerInternal, - mParserSupplier, mIntegrityFileManager, mHandler); diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java index 7e22d74c64e1..b1d658cb1e86 100644 --- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java @@ -25,6 +25,7 @@ import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK; import static android.media.projection.ReviewGrantedConsentResult.UNKNOWN; +import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS; import static android.view.ContentRecordingSession.TARGET_UID_FULL_SCREEN; import static android.view.ContentRecordingSession.TARGET_UID_UNKNOWN; import static android.view.ContentRecordingSession.createDisplaySession; @@ -80,6 +81,7 @@ import android.os.test.TestLooper; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings; import android.testing.TestableContext; import android.view.ContentRecordingSession; import android.view.ContentRecordingSession.RecordContent; @@ -372,6 +374,50 @@ public class MediaProjectionManagerServiceTest { }); } + @EnableFlags(android.companion.virtualdevice.flags + .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS) + @Test + public void testCreateProjection_keyguardLocked_screenshareProtectionsDisabled() + throws NameNotFoundException { + MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); + int value = Settings.Global.getInt(mContext.getContentResolver(), + DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0); + try { + Settings.Global.putInt(mContext.getContentResolver(), + DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 1); + doReturn(true).when(mKeyguardManager).isKeyguardLocked(); + + doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission( + RECORD_SENSITIVE_CONTENT, projection.packageName); + + projection.start(mIMediaProjectionCallback); + projection.notifyVirtualDisplayCreated(10); + + // The projection was started because it was allowed to capture the keyguard. + assertThat(mService.getActiveProjectionInfo()).isNotNull(); + } finally { + Settings.Global.putInt(mContext.getContentResolver(), + DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, value); + } + } + + @EnableFlags(android.companion.virtualdevice.flags + .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS) + @Test + public void testCreateProjection_keyguardLocked_noDisplayCreated() + throws NameNotFoundException { + MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); + doReturn(true).when(mKeyguardManager).isKeyguardLocked(); + + doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission( + RECORD_SENSITIVE_CONTENT, projection.packageName); + + projection.start(mIMediaProjectionCallback); + + // The projection was started because it was allowed to capture the keyguard. + assertThat(mService.getActiveProjectionInfo()).isNotNull(); + } + @Test public void testCreateProjection_attemptReuse_noPriorProjectionGrant() throws NameNotFoundException { @@ -485,6 +531,7 @@ public class MediaProjectionManagerServiceTest { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(service); projection.start(mIMediaProjectionCallback); + projection.notifyVirtualDisplayCreated(10); assertThat(service.getActiveProjectionInfo()).isNotNull(); @@ -507,6 +554,7 @@ public class MediaProjectionManagerServiceTest { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(service); projection.start(mIMediaProjectionCallback); + projection.notifyVirtualDisplayCreated(10); assertThat(service.getActiveProjectionInfo()).isNotNull(); diff --git a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java index a54501029712..f45eddcf4480 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.when; import android.database.Cursor; import android.database.MatrixCursor; +import android.database.sqlite.SQLiteException; import android.net.Uri; import android.provider.CallLog.Calls; import android.test.mock.MockContentProvider; @@ -58,6 +59,7 @@ public final class CallLogQueryHelperTest { private MatrixCursor mCursor; private EventConsumer mEventConsumer; private CallLogQueryHelper mHelper; + private CallLogContentProvider mCallLogContentProvider; @Before public void setUp() { @@ -66,7 +68,8 @@ public final class CallLogQueryHelperTest { mCursor = new MatrixCursor(CALL_LOG_COLUMNS); MockContentResolver contentResolver = new MockContentResolver(); - contentResolver.addProvider(CALL_LOG_AUTHORITY, new CallLogContentProvider()); + mCallLogContentProvider = new CallLogContentProvider(); + contentResolver.addProvider(CALL_LOG_AUTHORITY, mCallLogContentProvider); when(mContext.getContentResolver()).thenReturn(contentResolver); mEventConsumer = new EventConsumer(); @@ -80,6 +83,12 @@ public final class CallLogQueryHelperTest { } @Test + public void testQueryWithSQLiteException() { + mCallLogContentProvider.setThrowSQLiteException(true); + assertFalse(mHelper.querySince(50L)); + } + + @Test public void testQueryIncomingCall() { mCursor.addRow(new Object[] { NORMALIZED_PHONE_NUMBER, /* date= */ 100L, /* duration= */ 30L, @@ -159,11 +168,20 @@ public final class CallLogQueryHelperTest { } private class CallLogContentProvider extends MockContentProvider { + private boolean mThrowSQLiteException = false; @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + if (mThrowSQLiteException) { + throw new SQLiteException(); + } + return mCursor; } + + public void setThrowSQLiteException(boolean throwException) { + this.mThrowSQLiteException = throwException; + } } } diff --git a/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java index 16a02b678511..1daee39ce9de 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java @@ -99,6 +99,14 @@ public final class ContactsQueryHelperTest { } @Test + public void testQueryOtherException_returnsFalse() { + contentProvider.setThrowOtherException(true); + + Uri contactUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, CONTACT_LOOKUP_KEY); + assertFalse(mHelper.query(contactUri.toString())); + } + + @Test public void testQueryIllegalArgumentException_returnsFalse() { contentProvider.setThrowIllegalArgumentException(true); @@ -152,6 +160,13 @@ public final class ContactsQueryHelperTest { } @Test + public void testQueryWithPhoneNumber_otherExceptionReturnsFalse() { + contentProvider.setThrowOtherException(true); + String contactUri = "tel:" + PHONE_NUMBER; + assertFalse(mHelper.query(contactUri)); + } + + @Test public void testQueryWithEmail() { mContactsLookupCursor.addRow(new Object[] { /* id= */ 11, CONTACT_LOOKUP_KEY, /* starred= */ 1, /* hasPhoneNumber= */ 0 }); @@ -188,6 +203,7 @@ public final class ContactsQueryHelperTest { private Map<Uri, Cursor> mUriPrefixToCursorMap = new ArrayMap<>(); private boolean mThrowSQLiteException = false; private boolean mThrowIllegalArgumentException = false; + private boolean mThrowOtherException = false; @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, @@ -198,6 +214,9 @@ public final class ContactsQueryHelperTest { if (mThrowIllegalArgumentException) { throw new IllegalArgumentException(); } + if (mThrowOtherException) { + throw new ArrayIndexOutOfBoundsException(); + } for (Uri prefixUri : mUriPrefixToCursorMap.keySet()) { if (uri.isPathPrefixMatch(prefixUri)) { @@ -215,6 +234,10 @@ public final class ContactsQueryHelperTest { this.mThrowIllegalArgumentException = throwException; } + public void setThrowOtherException(boolean throwException) { + this.mThrowOtherException = throwException; + } + private void registerCursor(Uri uriPrefix, Cursor cursor) { mUriPrefixToCursorMap.put(uriPrefix, cursor); } diff --git a/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java index 7730890e1486..9f4a43df6de8 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.when; import android.database.Cursor; import android.database.MatrixCursor; +import android.database.sqlite.SQLiteException; import android.net.Uri; import android.provider.Telephony.BaseMmsColumns; import android.provider.Telephony.Mms; @@ -63,6 +64,7 @@ public final class MmsQueryHelperTest { private final List<MatrixCursor> mAddrCursors = new ArrayList<>(); private EventConsumer mEventConsumer; private MmsQueryHelper mHelper; + private MmsContentProvider mMmsContentProvider; @Before public void setUp() { @@ -73,7 +75,8 @@ public final class MmsQueryHelperTest { mAddrCursors.add(new MatrixCursor(ADDR_COLUMNS)); MockContentResolver contentResolver = new MockContentResolver(); - contentResolver.addProvider(MMS_AUTHORITY, new MmsContentProvider()); + mMmsContentProvider = new MmsContentProvider(); + contentResolver.addProvider(MMS_AUTHORITY, mMmsContentProvider); when(mContext.getContentResolver()).thenReturn(contentResolver); mEventConsumer = new EventConsumer(); @@ -87,6 +90,12 @@ public final class MmsQueryHelperTest { } @Test + public void testQueryWithSQLiteException() { + mMmsContentProvider.setThrowSQLiteException(true); + assertFalse(mHelper.querySince(50_000L)); + } + + @Test public void testQueryIncomingMessage() { mMmsCursor.addRow(new Object[] { /* id= */ 0, /* date= */ 100L, /* msgBox= */ BaseMmsColumns.MESSAGE_BOX_INBOX }); @@ -159,10 +168,15 @@ public final class MmsQueryHelperTest { } private class MmsContentProvider extends MockContentProvider { + private boolean mThrowSQLiteException = false; @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + if (mThrowSQLiteException) { + throw new SQLiteException(); + } + List<String> segments = uri.getPathSegments(); if (segments.size() == 2 && "addr".equals(segments.get(1))) { int messageId = Integer.valueOf(segments.get(0)); @@ -170,5 +184,9 @@ public final class MmsQueryHelperTest { } return mMmsCursor; } + + public void setThrowSQLiteException(boolean throwException) { + this.mThrowSQLiteException = throwException; + } } } diff --git a/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java index 5cb8cb4fe9f1..09a0dff77eb0 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.when; import android.database.Cursor; import android.database.MatrixCursor; +import android.database.sqlite.SQLiteException; import android.net.Uri; import android.provider.Telephony.Sms; import android.provider.Telephony.TextBasedSmsColumns; @@ -59,6 +60,7 @@ public final class SmsQueryHelperTest { private MatrixCursor mSmsCursor; private EventConsumer mEventConsumer; private SmsQueryHelper mHelper; + private SmsContentProvider mSmsContentProvider; @Before public void setUp() { @@ -67,7 +69,8 @@ public final class SmsQueryHelperTest { mSmsCursor = new MatrixCursor(SMS_COLUMNS); MockContentResolver contentResolver = new MockContentResolver(); - contentResolver.addProvider(SMS_AUTHORITY, new SmsContentProvider()); + mSmsContentProvider = new SmsContentProvider(); + contentResolver.addProvider(SMS_AUTHORITY, mSmsContentProvider); when(mContext.getContentResolver()).thenReturn(contentResolver); mEventConsumer = new EventConsumer(); @@ -130,6 +133,12 @@ public final class SmsQueryHelperTest { assertEquals(110L, events.get(1).getTimestamp()); } + @Test + public void testQueryWithSQLiteException() { + mSmsContentProvider.setThrowSQLiteException(true); + assertFalse(mHelper.querySince(50L)); + } + private class EventConsumer implements BiConsumer<String, Event> { private final Map<String, List<Event>> mEventMap = new ArrayMap<>(); @@ -141,11 +150,19 @@ public final class SmsQueryHelperTest { } private class SmsContentProvider extends MockContentProvider { + private boolean mThrowSQLiteException = false; @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + if (mThrowSQLiteException) { + throw new SQLiteException(); + } return mSmsCursor; } + + public void setThrowSQLiteException(boolean throwException) { + this.mThrowSQLiteException = throwException; + } } } diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java new file mode 100644 index 000000000000..727b435aa743 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.advancedprotection; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.RemoteException; +import android.os.test.FakePermissionEnforcer; +import android.os.test.TestLooper; +import android.provider.Settings; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class AdvancedProtectionServiceTest { + private AdvancedProtectionService mService; + private FakePermissionEnforcer mPermissionEnforcer; + private Context mContext; + + @Before + @SuppressLint("VisibleForTests") + public void setup() throws Settings.SettingNotFoundException { + mContext = mock(Context.class); + mPermissionEnforcer = new FakePermissionEnforcer(); + mPermissionEnforcer.grant(Manifest.permission.SET_ADVANCED_PROTECTION_MODE); + mPermissionEnforcer.grant(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); + + AdvancedProtectionService.AdvancedProtectionStore store = + new AdvancedProtectionService.AdvancedProtectionStore(mContext) { + private boolean mEnabled = false; + + @Override + boolean retrieve() { + return mEnabled; + } + + @Override + void store(boolean enabled) { + this.mEnabled = enabled; + } + }; + + mService = new AdvancedProtectionService(mContext, store, new TestLooper().getLooper(), + mPermissionEnforcer); + } + + @Test + public void testEnableProtection() throws RemoteException { + mService.setAdvancedProtectionEnabled(true); + assertTrue(mService.isAdvancedProtectionEnabled()); + } + + @Test + public void testDisableProtection() throws RemoteException { + mService.setAdvancedProtectionEnabled(false); + assertFalse(mService.isAdvancedProtectionEnabled()); + } + + @Test + public void testSetProtection_withoutPermission() { + mPermissionEnforcer.revoke(Manifest.permission.SET_ADVANCED_PROTECTION_MODE); + assertThrows(SecurityException.class, () -> mService.setAdvancedProtectionEnabled(true)); + } + + @Test + public void testGetProtection_withoutPermission() { + mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); + assertThrows(SecurityException.class, () -> mService.isAdvancedProtectionEnabled()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt new file mode 100644 index 000000000000..22d894a5a739 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.systemconfig + +import android.content.Context +import android.content.pm.FeatureInfo +import android.util.ArrayMap +import android.util.Xml + +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.SystemConfig +import com.google.common.truth.Truth.assertThat +import org.junit.runner.RunWith +import org.junit.Rule + +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.junit.runners.JUnit4 + +@SmallTest +@RunWith(JUnit4::class) +class SystemConfigReadOnlyFeaturesTest { + + companion object { + private const val FEATURE_ONE = "feature.test.1" + private const val FEATURE_TWO = "feature.test.2" + private const val FEATURE_RUNTIME_AVAILABLE_TEMPLATE = + """ + <permissions> + <feature name="%s" /> + </permissions> + """ + private const val FEATURE_RUNTIME_DISABLED_TEMPLATE = + """ + <permissions> + <Disabled-feature name="%s" /> + </permissions> + """ + + fun featureInfo(featureName: String) = FeatureInfo().apply { name = featureName } + } + + private val context: Context = InstrumentationRegistry.getInstrumentation().context + + @get:Rule + val tempFolder = TemporaryFolder(context.filesDir) + + private val injector = TestInjector() + + private var uniqueCounter = 0 + + @Test + fun empty() { + assertFeatures().isEmpty() + } + + @Test + fun readOnlyEnabled() { + addReadOnlyEnabledFeature(FEATURE_ONE) + addReadOnlyEnabledFeature(FEATURE_TWO) + + assertFeatures().containsAtLeast(FEATURE_ONE, FEATURE_TWO) + } + + @Test + fun readOnlyAndRuntimeEnabled() { + addReadOnlyEnabledFeature(FEATURE_ONE) + addRuntimeEnabledFeature(FEATURE_TWO) + + // No issues with matching availability. + assertFeatures().containsAtLeast(FEATURE_ONE, FEATURE_TWO) + } + + @Test + fun readOnlyEnabledRuntimeDisabled() { + addReadOnlyEnabledFeature(FEATURE_ONE) + addRuntimeDisabledFeature(FEATURE_ONE) + + // Read-only feature availability should take precedence. + assertFeatures().contains(FEATURE_ONE) + } + + @Test + fun readOnlyDisabled() { + addReadOnlyDisabledFeature(FEATURE_ONE) + + assertFeatures().doesNotContain(FEATURE_ONE) + } + + @Test + fun readOnlyAndRuntimeDisabled() { + addReadOnlyDisabledFeature(FEATURE_ONE) + addRuntimeDisabledFeature(FEATURE_ONE) + + // No issues with matching (un)availability. + assertFeatures().doesNotContain(FEATURE_ONE) + } + + @Test + fun readOnlyDisabledRuntimeEnabled() { + addReadOnlyDisabledFeature(FEATURE_ONE) + addRuntimeEnabledFeature(FEATURE_ONE) + addRuntimeEnabledFeature(FEATURE_TWO) + + // Read-only feature (un)availability should take precedence. + assertFeatures().doesNotContain(FEATURE_ONE) + assertFeatures().contains(FEATURE_TWO) + } + + fun addReadOnlyEnabledFeature(featureName: String) { + injector.readOnlyEnabledFeatures[featureName] = featureInfo(featureName) + } + + fun addReadOnlyDisabledFeature(featureName: String) { + injector.readOnlyDisabledFeatures.add(featureName) + } + + fun addRuntimeEnabledFeature(featureName: String) { + FEATURE_RUNTIME_AVAILABLE_TEMPLATE.format(featureName).write() + } + + fun addRuntimeDisabledFeature(featureName: String) { + FEATURE_RUNTIME_DISABLED_TEMPLATE.format(featureName).write() + } + + private fun String.write() = tempFolder.root.resolve("${uniqueCounter++}.xml") + .writeText(this.trimIndent()) + + private fun assertFeatures() = assertThat(availableFeatures().keys) + + private fun availableFeatures() = SystemConfig(false, injector).apply { + val parser = Xml.newPullParser() + readPermissions(parser, tempFolder.root, /*Grant all permission flags*/ 0.inv()) + }.let { it.availableFeatures } + + internal class TestInjector() : SystemConfig.Injector() { + val readOnlyEnabledFeatures = ArrayMap<String, FeatureInfo>() + val readOnlyDisabledFeatures = mutableSetOf<String>() + + override fun isReadOnlySystemEnabledFeature(featureName: String, version: Int): Boolean { + return readOnlyEnabledFeatures.containsKey(featureName) + } + + override fun isReadOnlySystemDisabledFeature(featureName: String, version: Int): Boolean { + return readOnlyDisabledFeatures.contains(featureName) + } + + override fun getReadOnlySystemEnabledFeatures(): ArrayMap<String, FeatureInfo> { + return readOnlyEnabledFeatures + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 89056cc34795..9a7abd43aab6 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -93,7 +93,10 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.Pair; @@ -111,6 +114,7 @@ import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import org.junit.After; import org.junit.Before; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -185,6 +189,9 @@ public class AppStandbyControllerTests { private static final Random sRandom = new Random(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private MyInjector mInjector; private AppStandbyController mController; @@ -894,7 +901,8 @@ public class AppStandbyControllerTests { } @Test - public void testScreenTimeAndBuckets() throws Exception { + @DisableFlags(Flags.FLAG_SCREEN_TIME_BYPASS) + public void testScreenTimeAndBuckets_Legacy() throws Exception { mInjector.setDisplayOn(false); assertTimeout(mController, 0, STANDBY_BUCKET_NEVER); @@ -917,6 +925,31 @@ public class AppStandbyControllerTests { } @Test + @EnableFlags(Flags.FLAG_SCREEN_TIME_BYPASS) + public void testScreenTimeAndBuckets_Bypass() throws Exception { + mInjector.setDisplayOn(false); + + assertTimeout(mController, 0, STANDBY_BUCKET_NEVER); + + reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); + + // ACTIVE bucket + assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE); + + // WORKING_SET bucket + assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET); + + // RARE bucket, should failed due to timeout hasn't reached yet. + mInjector.mElapsedRealtime = RARE_THRESHOLD - 1; + mController.checkIdleStates(USER_ID); + waitAndAssertNotBucket(STANDBY_BUCKET_RARE, PACKAGE_1); + + mInjector.setDisplayOn(true); + // screen on time doesn't count. + assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE); + } + + @Test public void testForcedIdle() throws Exception { mController.forceIdleState(PACKAGE_1, USER_ID, true); waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1); diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java index c247c08c8010..3b0cb4ad8779 100644 --- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java @@ -68,6 +68,7 @@ import static org.testng.Assert.assertThrows; import android.Manifest; import android.app.Activity; +import android.app.ActivityManager; import android.app.AlarmManager; import android.app.Flags; import android.app.IOnProjectionStateChangedListener; @@ -247,6 +248,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { mInjector = spy(new TestInjector()); mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true, mTwilightManager, mInjector); + // Initialize the current user. + mUiManagerService.setCurrentUser(ActivityManager.getCurrentUser()); try { mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); } catch (SecurityException e) {/* ignore for permission denial */} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index becb9fdabfcf..e845d80b412a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4838,7 +4838,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { null, mPkg, Process.myUserHandle()); verify(mPreferencesHelper, times(1)).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4856,7 +4856,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } verify(mPreferencesHelper, never()).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4871,7 +4871,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { null, mPkg, Process.myUserHandle()); verify(mPreferencesHelper, times(1)).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4891,7 +4891,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } verify(mPreferencesHelper, never()).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4913,7 +4913,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } verify(mPreferencesHelper, never()).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -17062,4 +17062,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertThat(mService.hasFlag(captor.getValue().getNotification().flags, FLAG_PROMOTED_ONGOING)).isFalse(); } + + @Test + @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) + public void testAppCannotUseReservedBundleChannels() throws Exception { + mBinderService.getBubblePreferenceForPackage(mPkg, mUid); + NotificationChannel news = mBinderService.getNotificationChannel( + mPkg, mContext.getUserId(), mPkg, NEWS_ID); + assertThat(news).isNotNull(); + + NotificationRecord nr = generateNotificationRecord(news); + mBinderService.enqueueNotificationWithTag(mPkg, mPkg, nr.getSbn().getTag(), + nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); + waitForIdle(); + + assertThat(mService.mNotificationList).isEmpty(); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 404ede676f02..b92bdb5f3e6e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -2547,7 +2547,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { // Returns only non-deleted channels List<NotificationChannel> channels = - mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList(); + mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true).getList(); // Default channel + non-deleted channel + system defaults assertEquals(notificationClassification() ? 6 : 2, channels.size()); for (NotificationChannel nc : channels) { @@ -2557,7 +2557,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { } // Returns deleted channels too - channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList(); + channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true).getList(); // Includes system channel(s) assertEquals(notificationClassification() ? 7 : 3, channels.size()); for (NotificationChannel nc : channels) { @@ -3199,7 +3199,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.permanentlyDeleteNotificationChannels(PKG_N_MR1, UID_N_MR1); // Only default channel remains - assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size()); + assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true) + .getList().size()); } @Test @@ -3315,12 +3316,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { // user 0 records remain for (int i = 0; i < user0Uids.length; i++) { assertEquals(notificationClassification() ? 5 : 1, - mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false) + mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true) .getList().size()); } // user 1 records are gone for (int i = 0; i < user1Uids.length; i++) { - assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false) + assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false, true) .getList().size()); } } @@ -3337,7 +3338,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { new int[]{UID_N_MR1})); assertEquals(0, mHelper.getNotificationChannels( - PKG_N_MR1, UID_N_MR1, true).getList().size()); + PKG_N_MR1, UID_N_MR1, true, true).getList().size()); // Not deleted mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false, @@ -3346,7 +3347,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{UID_N_MR1})); assertEquals(notificationClassification() ? 6 : 2, - mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size()); + mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true) + .getList().size()); } @Test @@ -3405,7 +3407,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertTrue(mHelper.canShowBadge(PKG_O, UID_O)); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); - assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true).getList().size()); + assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size()); assertEquals(0, mHelper.getNotificationChannelGroups(PKG_O, UID_O).size()); NotificationChannel channel = getChannel(); @@ -3419,7 +3421,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testRecordDefaults() throws Exception { assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); assertEquals(notificationClassification() ? 5 : 1, - mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size()); + mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true) + .getList().size()); } @Test @@ -6363,6 +6366,15 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) + public void testGetNotificationChannels_omitBundleChannels() { + // do something that triggers settings creation for an app + mHelper.setShowBadge(PKG_O, UID_O, true); + + assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, false).getList()).isEmpty(); + } + + @Test + @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testNotificationBundles() { // do something that triggers settings creation for an app mHelper.setShowBadge(PKG_O, UID_O, true); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index b7821623855c..7f5da41bdf10 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -16,8 +16,6 @@ package com.android.server.vibrator; -import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED; - import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; @@ -28,6 +26,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -89,17 +88,14 @@ import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; -import android.platform.test.annotations.RequiresFlagsDisabled; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.HapticFeedbackConstants; import android.view.InputDevice; -import android.view.flags.Flags; import androidx.test.InstrumentationRegistry; @@ -168,9 +164,7 @@ public class VibratorManagerServiceTest { @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock; @@ -800,7 +794,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS) + @EnableFlags(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS) public void vibrate_thenDeniedAppOps_getsCancelled() throws Throwable { mockVibrators(1); VibratorManagerService service = createSystemReadyService(); @@ -894,7 +888,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS) + @EnableFlags(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS) public void vibrate_thenFgUserRequestsMute_getsCancelled() throws Throwable { mockVibrators(1); VibratorManagerService service = createSystemReadyService(); @@ -1331,6 +1325,37 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags(android.os.vibrator.Flags.FLAG_VIBRATION_PIPELINE_ENABLED) + public void vibrate_withPipelineFlagEnabledAndShortEffect_continuesOngoingEffect() + throws Exception { + assumeTrue(mVibrationConfig.getVibrationPipelineMaxDurationMs() > 0); + + mockVibrators(1); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + fakeVibrator.setSupportedPrimitives( + VibrationEffect.Composition.PRIMITIVE_CLICK, + VibrationEffect.Composition.PRIMITIVE_THUD); + fakeVibrator.setPrimitiveDuration( + mVibrationConfig.getVibrationPipelineMaxDurationMs() - 1); + VibratorManagerService service = createSystemReadyService(); + + HalVibration firstVibration = vibrateWithUid(service, /* uid= */ 123, + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose(), HAPTIC_FEEDBACK_ATTRS); + HalVibration secondVibration = vibrateWithUid(service, /* uid= */ 456, + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) + .compose(), HAPTIC_FEEDBACK_ATTRS); + secondVibration.waitForEnd(); + + assertThat(fakeVibrator.getAllEffectSegments()).hasSize(2); + assertThat(firstVibration.getStatus()).isEqualTo(Status.FINISHED); + assertThat(secondVibration.getStatus()).isEqualTo(Status.FINISHED); + } + + @Test public void vibrate_withInputDevices_vibratesInputDevices() throws Exception { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); @@ -1512,6 +1537,7 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API) public void performHapticFeedback_doesNotRequireVibrateOrBypassPermissions() throws Exception { // Deny permissions that would have been required for regular vibrations, and check that // the vibration proceed as expected to verify that haptic feedback does not need these @@ -1520,8 +1546,6 @@ public class VibratorManagerServiceTest { denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); - // Flag override to enable the scroll feedack constants to bypass interruption policies. - mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API); mHapticFeedbackVibrationMap.put( HapticFeedbackConstants.SCROLL_TICK, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); @@ -1544,6 +1568,10 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags({ + android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API, + android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED, + }) public void performHapticFeedbackForInputDevice_doesNotRequireVibrateOrBypassPermissions() throws Exception { // Deny permissions that would have been required for regular vibrations, and check that @@ -1553,9 +1581,6 @@ public class VibratorManagerServiceTest { denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); - // Flag override to enable the scroll feedback constants to bypass interruption policies. - mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API); - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); mHapticFeedbackVibrationMapSourceRotary.put( HapticFeedbackConstants.SCROLL_TICK, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); @@ -1628,12 +1653,14 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags({ + android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API, + android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED, + }) public void performHapticFeedbackForInputDevice_restrictedConstantsWithoutPermission_doesNotVibrate() throws Exception { // Deny permission to vibrate with restricted constants denyPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS); - mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API); - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); // Public constant, no permission required mHapticFeedbackVibrationMapSourceRotary.put( HapticFeedbackConstants.CONFIRM, @@ -1697,9 +1724,9 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags(android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED) public void performHapticFeedbackForInputDevice_restrictedConstantsWithPermission_playsVibration() throws Exception { - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); // Grant permission to vibrate with restricted constants grantPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS); // Public constant, no permission required @@ -1732,9 +1759,11 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags({ + android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API, + android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED, + }) public void performHapticFeedback_doesNotVibrateWhenVibratorInfoNotReady() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API); - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); denyPermission(android.Manifest.permission.VIBRATE); mHapticFeedbackVibrationMap.put( HapticFeedbackConstants.KEYBOARD_TAP, @@ -1767,9 +1796,11 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags({ + android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API, + android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED, + }) public void performHapticFeedback_doesNotVibrateForInvalidConstant() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API); - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); denyPermission(android.Manifest.permission.VIBRATE); mockVibrators(1); VibratorManagerService service = createSystemReadyService(); @@ -1791,7 +1822,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) public void vibrate_vendorEffectsWithoutPermission_doesNotVibrate() throws Exception { // Deny permission to vibrate with vendor effects denyPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS); @@ -1816,7 +1847,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) public void vibrate_vendorEffectsWithPermission_successful() throws Exception { // Grant permission to vibrate with vendor effects grantPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS); @@ -1904,7 +1935,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) + @EnableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) public void vibrate_withAdaptiveHaptics_appliesCorrectAdaptiveScales() throws Exception { // Keep user settings the same as device default so only adaptive scale is applied. setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, @@ -1947,7 +1978,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled({ + @EnableFlags({ android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED, android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS, }) @@ -2418,7 +2449,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) + @EnableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) public void onExternalVibration_withAdaptiveHaptics_returnsCorrectAdaptiveScales() { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL, @@ -2465,7 +2496,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsDisabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) + @DisableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) public void onExternalVibration_withAdaptiveHapticsFlagDisabled_alwaysReturnScaleNone() { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL, @@ -2585,7 +2616,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS) + @EnableFlags(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS) public void onExternalVibration_thenFgUserRequestsMute_doNotCancelVibration() throws Throwable { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); @@ -3105,9 +3136,20 @@ public class VibratorManagerServiceTest { return vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs); } + private HalVibration vibrateWithUid(VibratorManagerService service, int uid, + VibrationEffect effect, VibrationAttributes attrs) { + return vibrateWithUidAndDevice(service, uid, Context.DEVICE_ID_DEFAULT, + CombinedVibration.createParallel(effect), attrs); + } + private HalVibration vibrateWithDevice(VibratorManagerService service, int deviceId, CombinedVibration effect, VibrationAttributes attrs) { - HalVibration vib = service.vibrateWithPermissionCheck(UID, deviceId, PACKAGE_NAME, effect, + return vibrateWithUidAndDevice(service, UID, deviceId, effect, attrs); + } + + private HalVibration vibrateWithUidAndDevice(VibratorManagerService service, int uid, + int deviceId, CombinedVibration effect, VibrationAttributes attrs) { + HalVibration vib = service.vibrateWithPermissionCheck(uid, deviceId, PACKAGE_NAME, effect, attrs, "some reason", service); if (vib != null) { mPendingVibrations.add(vib); diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java index 6dc1b10ec930..75a9cedfd8c4 100644 --- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java +++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java @@ -80,6 +80,7 @@ public final class FakeVibratorControllerProvider { private float[] mFrequenciesHz; private float[] mOutputAccelerationsGs; private long mVendorEffectDuration = EFFECT_DURATION; + private long mPrimitiveDuration = EFFECT_DURATION; void recordEffectSegment(long vibrationId, VibrationEffectSegment segment) { mEffectSegments.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(segment); @@ -171,7 +172,7 @@ public final class FakeVibratorControllerProvider { } long duration = 0; for (PrimitiveSegment primitive : primitives) { - duration += EFFECT_DURATION + primitive.getDelay(); + duration += mPrimitiveDuration + primitive.getDelay(); recordEffectSegment(vibrationId, primitive); } applyLatency(mOnLatency); @@ -381,6 +382,11 @@ public final class FakeVibratorControllerProvider { mVendorEffectDuration = durationMs; } + /** Set the duration of primitives in fake vibrator hardware. */ + public void setPrimitiveDuration(long primitiveDuration) { + mPrimitiveDuration = primitiveDuration; + } + /** * Set the maximum number of envelope effects control points supported in fake vibrator * hardware. diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index ab00bfdf41ae..1f167761fc06 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -71,6 +71,7 @@ android_test { "CtsSurfaceValidatorLib", "service-sdksandbox.impl", "com.android.window.flags.window-aconfig-java", + "android.view.inputmethod.flags-aconfig-java", "flag-junit", ], diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index d0080d29f82b..d5f86b6feac8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -1261,6 +1261,33 @@ public class ActivityStarterTests extends WindowTestsBase { } @Test + public void testLaunchAdjacentDisabled() { + final ActivityStarter starter = + prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetRootTask */); + final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityOptions options = ActivityOptions.makeBasic(); + final ActivityRecord[] outActivity = new ActivityRecord[1]; + + // Activity must not land on split-screen task if currently not in split-screen mode. + starter.setActivityOptions(options.toBundle()) + .setReason("testLaunchAdjacentDisabled") + .setOutActivity(outActivity).execute(); + assertThat(outActivity[0].inMultiWindowMode()).isFalse(); + + // Move activity to split-screen-primary task and make sure it has the focus. + TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayContent()); + top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM); + top.getRootTask().moveToFront("testLaunchAdjacentDisabled"); + top.getRootTask().setLaunchAdjacentDisabled(true); + + // Ensure activity does not launch into split-screen-secondary when launch adjacent is + // disabled + startActivityInner(starter, outActivity[0], top, options, null /* inTask */, + null /* taskFragment*/); + assertThat(outActivity[0].isDescendantOf(splitOrg.mSecondary)).isFalse(); + } + + @Test public void testTransientLaunchWithKeyguard() { final ActivityStarter starter = prepareStarter(0 /* flags */); final ActivityRecord target = new ActivityBuilder(mAtm).setCreateTask(true).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index c176658da847..e0b29c937381 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -382,13 +382,11 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { @Test public void testResumeNextActivityOnCrashedAppDied() { - mSupervisor.beginDeferResume(); final ActivityRecord homeActivity = new ActivityBuilder(mAtm) .setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask()) .build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity.setState(RESUMED, "test"); - mSupervisor.endDeferResume(); assertEquals(activity.app, mAtm.mInternal.getTopApp()); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index d2cf03dd4b9a..ee56210e278d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -41,11 +41,13 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.verify; import android.app.StatusBarManager; +import android.graphics.Insets; import android.graphics.Rect; import android.os.Binder; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; import android.view.InsetsFrameProvider; import android.view.InsetsSource; import android.view.InsetsSourceControl; @@ -525,6 +527,59 @@ public class InsetsPolicyTest extends WindowTestsBase { assertTrue(win1.getWindowFrames().hasInsetsChanged()); } + /** + * This test verifies that after setting {@link WindowContainer#mExcludeInsetsTypes}, the IME + * insets have a height of zero (applied in {@link InsetsPolicy#adjustVisibilityForIme}). + */ + @RequiresFlagsEnabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER) + @SetupWindows(addWindows = W_INPUT_METHOD) + @Test + public void testExcludeImeInsets() { + final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); + final InsetsSource imeSource = new InsetsSource(ID_IME, ime()); + imeSource.setVisible(true); + mImeWindow.mHasSurface = true; + + final WindowState win = addWindow(TYPE_APPLICATION, "win1"); + win.setRequestedVisibleTypes(0, ime()); + + win.mAboveInsetsState.addSource(imeSource); + win.mHasSurface = true; + + DisplayContentTests.performLayout(mDisplayContent); + // IME should cover half of the app's window + final var winFrame = win.getFrame(); + imeSource.setFrame(winFrame.left, winFrame.bottom / 2, winFrame.right, winFrame.bottom); + imeSource.setVisibleFrame(imeSource.getFrame()); + DisplayContentTests.performLayout(mDisplayContent); + + assertTrue(mImeWindow.isVisible()); + assertTrue(win.isVisible()); + + displayPolicy.beginPostLayoutPolicyLw(); + displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null); + displayPolicy.finishPostLayoutPolicyLw(); + + final var imeInsetsShown = win.getInsetsState().calculateInsets(win.getFrame(), ime(), + true); + assertEquals(new Rect(0, 0, 0, winFrame.bottom / 2), imeInsetsShown.toRect()); + + + // Now we're setting the excludedInsetsTypes for the IME. The IME is still showing, but + // in this case, InsetsPolicy#adjustVisibilityForIme will override and dispatch IME + // insets with zero height. + win.setExcludeInsetsTypes(ime()); + + displayPolicy.beginPostLayoutPolicyLw(); + displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null); + displayPolicy.finishPostLayoutPolicyLw(); + + final var imeInsetsHidden = win.getInsetsState().calculateInsets(win.getFrame(), ime(), + true); + assertEquals(Insets.NONE, imeInsetsHidden); + } + + private WindowState addNavigationBar() { final Binder owner = new Binder(); final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "navBar"); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index 457058849fca..79967b861ea5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -30,6 +30,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.graphics.Insets; +import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.InsetsSource; @@ -260,6 +261,27 @@ public class InsetsSourceProviderTest extends WindowTestsBase { } @Test + public void testUpdateInsetsControlPosition() { + final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); + + final WindowState ime1 = createWindow(null, TYPE_INPUT_METHOD, "ime1"); + ime1.getFrame().set(new Rect(0, 0, 0, 0)); + mImeProvider.setWindowContainer(ime1, null, null); + mImeProvider.updateControlForTarget(target, false /* force */, null /* statsToken */); + ime1.getFrame().set(new Rect(0, 400, 500, 500)); + mImeProvider.updateInsetsControlPosition(ime1); + assertEquals(new Point(0, 400), mImeProvider.getControl(target).getSurfacePosition()); + + final WindowState ime2 = createWindow(null, TYPE_INPUT_METHOD, "ime2"); + ime2.getFrame().set(new Rect(0, 0, 0, 0)); + mImeProvider.setWindowContainer(ime2, null, null); + mImeProvider.updateControlForTarget(target, false /* force */, null /* statsToken */); + ime2.getFrame().set(new Rect(0, 400, 500, 500)); + mImeProvider.updateInsetsControlPosition(ime2); + assertEquals(new Point(0, 400), mImeProvider.getControl(target).getSurfacePosition()); + } + + @Test public void testSetRequestedVisibleTypes() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index c1edae91aabb..c30f70ee2903 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -183,15 +183,32 @@ public class SizeCompatTests extends WindowTestsBase { DeviceConfig.setProperties(mInitialConstrainDisplayApisFlags); } - private void setUpApp(DisplayContent display) { - mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build(); - mActivity = mTask.getTopNonFinishingActivity(); + private ActivityRecord setUpApp(DisplayContent display) { + return setUpApp(display, null /* appBuilder */); + } + + private ActivityRecord setUpApp(DisplayContent display, ActivityBuilder appBuilder) { + // Use the real package name (com.android.frameworks.wmtests) so that + // EnableCompatChanges/DisableCompatChanges can take effect. + // Otherwise the fake WindowTestsBase.DEFAULT_COMPONENT_PACKAGE_NAME will make + // PlatformCompat#isChangeEnabledByPackageName always return default value. + final ComponentName componentName = ComponentName.createRelative( + mContext, SizeCompatTests.class.getName()); + mTask = new TaskBuilder(mSupervisor).setDisplay(display).setComponent(componentName) + .build(); + final ActivityBuilder builder = appBuilder != null ? appBuilder : new ActivityBuilder(mAtm); + mActivity = builder.setTask(mTask).setComponent(componentName).build(); doReturn(false).when(mActivity).isImmersiveMode(any()); + return mActivity; + } + + private ActivityRecord setUpDisplaySizeWithApp(int dw, int dh) { + return setUpDisplaySizeWithApp(dw, dh, null /* appBuilder */); } - private void setUpDisplaySizeWithApp(int dw, int dh) { + private ActivityRecord setUpDisplaySizeWithApp(int dw, int dh, ActivityBuilder appBuilder) { final TestDisplayContent.Builder builder = new TestDisplayContent.Builder(mAtm, dw, dh); - setUpApp(builder.build()); + return setUpApp(builder.build(), appBuilder); } @Test @@ -352,15 +369,13 @@ public class SizeCompatTests extends WindowTestsBase { .setCanRotate(false) .setNotch(cutoutHeight) .build(); - setUpApp(display); display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mWm.mAppCompatConfiguration.setLetterboxVerticalPositionMultiplier(0.5f); mWm.mAppCompatConfiguration.setIsVerticalReachabilityEnabled(true); - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) - .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE) - .build(); + .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)); doReturn(true).when(activity).isImmersiveMode(any()); addWindowToActivity(activity); @@ -424,17 +439,16 @@ public class SizeCompatTests extends WindowTestsBase { @Test @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() { - final int notchHeight = 100; - setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build()); - - final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds(); final float aspectRatio = 1.2f; - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityBuilder activityBuilder = new ActivityBuilder(mAtm) .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) .setMinAspectRatio(aspectRatio) .setMaxAspectRatio(aspectRatio) - .setResizeMode(RESIZE_MODE_UNRESIZEABLE) - .build(); + .setResizeMode(RESIZE_MODE_UNRESIZEABLE); + final int notchHeight = 100; + final ActivityRecord activity = setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800) + .setNotch(notchHeight).build(), activityBuilder); + final Rect displayBounds = activity.mDisplayContent.getWindowConfiguration().getBounds(); final Rect appBounds = activity.getWindowConfiguration().getAppBounds(); // The parent configuration doesn't change since the first resolved configuration, so the @@ -1327,12 +1341,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideMinAspectRatioSmall_overridden() { final int dh = 1200; final int dw = 1000; - setUpDisplaySizeWithApp(dw, dh); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(dw, dh, new ActivityBuilder(mAtm) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); final Rect bounds = activity.getBounds(); assertEquals(dh, bounds.height()); @@ -1346,13 +1356,9 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideMinAspectRatioSmall_notOverridden() { final int dh = 1200; final int dw = 1000; - setUpDisplaySizeWithApp(dw, dh); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(dw, dh, new ActivityBuilder(mAtm) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)); // Activity's requested aspect ratio is larger than OVERRIDE_MIN_ASPECT_RATIO_SMALL, // so OVERRIDE_MIN_ASPECT_RATIO_SMALL is ignored. @@ -1366,12 +1372,8 @@ public class SizeCompatTests extends WindowTestsBase { @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) public void testOverrideMinAspectRatioMedium() { - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // The per-package override forces the activity into a 3:2 aspect ratio assertEquals(1200, activity.getBounds().height()); @@ -1388,13 +1390,8 @@ public class SizeCompatTests extends WindowTestsBase { final int notchHeight = 200; final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, dh) .setNotch(notchHeight).setSystemDecorations(true).build(); - mTask = new TaskBuilder(mSupervisor).setDisplay(display).build(); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setMinAspectRatio(2f) - .build(); + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).setMinAspectRatio(2f)); // The per-package override should have no effect, because the manifest aspect ratio is // larger (2:1) @@ -1411,13 +1408,9 @@ public class SizeCompatTests extends WindowTestsBase { @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) public void testOverrideMinAspectRatioLargerThanManifest() { - setUpDisplaySizeWithApp(1400, 1600); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setMinAspectRatio(1.1f) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1400, 1600, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .setMinAspectRatio(1.1f)); // The per-package override should have no effect, because the manifest aspect ratio is // larger (2:1) @@ -1430,12 +1423,8 @@ public class SizeCompatTests extends WindowTestsBase { @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) public void testOverrideMinAspectRatioLarge() { - setUpDisplaySizeWithApp(1500, 1600); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1500, 1600, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // The per-package override forces the activity into a 16:9 aspect ratio assertEquals(1600, activity.getBounds().height()); @@ -1449,13 +1438,8 @@ public class SizeCompatTests extends WindowTestsBase { ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) public void testOverrideMinAspectRatio_Both() { // If multiple override aspect ratios are set, we should use the largest one - - setUpDisplaySizeWithApp(1400, 1600); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1400, 1600, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // The per-package override forces the activity into a 16:9 aspect ratio assertEquals(1600, activity.getBounds().height()); @@ -1470,11 +1454,7 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() { // In this test, the activity's orientation isn't fixed to portrait, therefore the override // isn't applied. - - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200); // The per-package override should have no effect assertEquals(1200, activity.getBounds().height()); @@ -1497,13 +1477,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() { // In this test, the activity's orientation isn't fixed to portrait, therefore the override // isn't applied. - - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)); // The per-package override should have no effect assertEquals(1200, activity.getBounds().height()); @@ -1524,12 +1499,8 @@ public class SizeCompatTests extends WindowTestsBase { ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() { - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // The per-package override forces the activity into a 3:2 aspect ratio assertEquals(1200, activity.getBounds().height()); @@ -1550,10 +1521,7 @@ public class SizeCompatTests extends WindowTestsBase { ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY}) public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet() { - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200); // The per-package override forces the activity into a 3:2 aspect ratio assertEquals(1200, activity.getBounds().height()); @@ -1568,13 +1536,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape() { // In this test, the activity's orientation isn't fixed to portrait, therefore the override // isn't applied. - - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)); // The per-package override forces the activity into a 3:2 aspect ratio assertEquals(1000 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, @@ -1588,12 +1551,8 @@ public class SizeCompatTests extends WindowTestsBase { // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without // OVERRIDE_MIN_ASPECT_RATIO being also set. - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)); // The per-package override should have no effect assertEquals(1200, activity.getBounds().height()); @@ -1604,12 +1563,8 @@ public class SizeCompatTests extends WindowTestsBase { @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) public void testOverrideMinAspectRatioLargeForResizableAppInSplitScreen() { - setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, activity.getDisplayContent()); @@ -2462,10 +2417,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() { final int displayWidth = 1400; final int displayHeight = 1600; - setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setMinAspectRatio(1.1f) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight, + new ActivityBuilder(mAtm).setMinAspectRatio(1.1f)); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); @@ -2483,10 +2436,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideSplitScreenAspectRatioForUnresizablePortraitAppsFromLandscape() { final int displayWidth = 1600; final int displayHeight = 1400; - setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setMinAspectRatio(1.1f) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight, + new ActivityBuilder(mAtm).setMinAspectRatio(1.1f)); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); @@ -2505,10 +2456,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeApps() { final int displayWidth = 1400; final int displayHeight = 1600; - setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setMinAspectRatio(1.1f) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight, + new ActivityBuilder(mAtm).setMinAspectRatio(1.1f)); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); @@ -2527,10 +2476,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeAppsFromLandscape() { final int displayWidth = 1600; final int displayHeight = 1400; - setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setMinAspectRatio(1.1f) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight, + new ActivityBuilder(mAtm).setMinAspectRatio(1.1f)); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); @@ -2549,8 +2496,7 @@ public class SizeCompatTests extends WindowTestsBase { mAtm.mDevEnableNonResizableMultiWindow = true; final int screenWidth = 1800; final int screenHeight = 1000; - setUpDisplaySizeWithApp(screenWidth, screenHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(screenWidth, screenHeight); activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate real display with top insets. @@ -2585,8 +2531,7 @@ public class SizeCompatTests extends WindowTestsBase { mAtm.mDevEnableNonResizableMultiWindow = true; final int screenWidth = 1000; final int screenHeight = 1800; - setUpDisplaySizeWithApp(screenWidth, screenHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(screenWidth, screenHeight); activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate real display with top insets. @@ -2616,18 +2561,18 @@ public class SizeCompatTests extends WindowTestsBase { @Test @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN}) public void testOverrideMinAspectRatioExcludePortraitFullscreen() { - setUpDisplaySizeWithApp(2600, 1600); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + resizeDisplay(mDisplayContent, 2600, 1600); + mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); // Non-resizable portrait activity - prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + final ActivityRecord activity = setUpApp(mDisplayContent, new ActivityBuilder(mAtm) + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // At first, OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_FULLSCREEN does not apply, because the // display is in landscape @@ -2645,16 +2590,16 @@ public class SizeCompatTests extends WindowTestsBase { @Test @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN}) public void testOverrideMinAspectRatioExcludePortraitFullscreenNotApplied() { // In this test, the activity is not in fullscreen, so the override is not applied - setUpDisplaySizeWithApp(2600, 1600); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); + resizeDisplay(mDisplayContent, 2600, 1600); + mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + final ActivityRecord activity = setUpApp(mDisplayContent); final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, activity.getDisplayContent()); @@ -2983,6 +2928,7 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION}) public void testDisplayIgnoreOrientationRequest_orientationChangedToUnspecified() { // Set up a display in landscape and ignoring orientation request. setUpDisplaySizeWithApp(2800, 1400); @@ -3437,6 +3383,7 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION}) public void testSupportsNonResizableInSplitScreen_letterboxForAspectRatioRestriction() { // Support non resizable in multi window mAtm.mDevEnableNonResizableMultiWindow = true; @@ -4050,9 +3997,9 @@ public class SizeCompatTests extends WindowTestsBase { @Test @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION}) public void testPortraitCloseToSquareDisplayWithTaskbar_insetsOverridden_notLetterboxed() { + final DisplayContent display = mDisplayContent; // Set up portrait close to square display. - setUpDisplaySizeWithApp(2200, 2280); - final DisplayContent display = mActivity.mDisplayContent; + resizeDisplay(display, 2200, 2280); display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate insets, final app bounds are (0, 0, 2200, 2130) - landscape. final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, @@ -4067,9 +4014,8 @@ public class SizeCompatTests extends WindowTestsBase { display.sendNewConfiguration(); } - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // Activity should not be letterboxed and should have portrait app bounds even though // orientation is not respected with insets as insets have been decoupled. @@ -4085,9 +4031,9 @@ public class SizeCompatTests extends WindowTestsBase { @Test @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testPortraitCloseToSquareDisplayWithTaskbar_letterboxed() { + final DisplayContent display = mDisplayContent; // Set up portrait close to square display - setUpDisplaySizeWithApp(2200, 2280); - final DisplayContent display = mActivity.mDisplayContent; + resizeDisplay(display, 2200, 2280); display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, @@ -4102,9 +4048,8 @@ public class SizeCompatTests extends WindowTestsBase { display.sendNewConfiguration(); } - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); final Rect bounds = activity.getBounds(); // Activity should be letterboxed and should have portrait app bounds @@ -4116,9 +4061,9 @@ public class SizeCompatTests extends WindowTestsBase { @Test @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testFixedAspectRatioAppInPortraitCloseToSquareDisplay_notInSizeCompat() { - setUpDisplaySizeWithApp(2200, 2280); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - final DisplayContent dc = mActivity.mDisplayContent; + final DisplayContent dc = mDisplayContent; + resizeDisplay(dc, 2200, 2280); + dc.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "navbar"); @@ -4132,11 +4077,10 @@ public class SizeCompatTests extends WindowTestsBase { dc.sendNewConfiguration(); } - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityRecord activity = setUpApp(dc, new ActivityBuilder(mAtm) .setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) - .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE) - .build(); + .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)); // To force config to update again but with the same landscape orientation. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); @@ -4174,13 +4118,10 @@ public class SizeCompatTests extends WindowTestsBase { .setCutout(0, 100, 0, 150) .build(); - setUpApp(display); - - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) .setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setMaxAspectRatio(2.1f) - .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) - .build(); + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)); // The activity height is 2100 and the display's app bounds height is 2050, so the activity // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display @@ -4196,13 +4137,10 @@ public class SizeCompatTests extends WindowTestsBase { .setCutout(100, 0, 150, 0) .build(); - setUpApp(display); - - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) .setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setMaxAspectRatio(2.1f) - .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) - .build(); + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)); // The activity width is 2100 and the display's app bounds width is 2250, so the activity // can be aligned inside parentAppBounds assertEquals(activity.getBounds(), new Rect(175, 0, 2275, 1000)); @@ -4216,12 +4154,10 @@ public class SizeCompatTests extends WindowTestsBase { .setCutout(100, 0, 150, 0) .build(); - setUpApp(display); - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) .setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setMaxAspectRatio(2.1f) - .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) - .build(); + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)); // The activity width is 2100 and the display's app bounds width is 2050, so the activity // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display assertEquals(activity.getBounds(), display.getBounds()); @@ -5032,17 +4968,13 @@ public class SizeCompatTests extends WindowTestsBase { */ private ActivityRecord buildActivityRecord(boolean supportsSizeChanges, int resizeMode, @ScreenOrientation int screenOrientation) { - return getActivityBuilderOnSameTask() + return getActivityBuilderWithoutTask().setTask(mTask) .setResizeMode(resizeMode) .setSupportsSizeChanges(supportsSizeChanges) .setScreenOrientation(screenOrientation) .build(); } - private ActivityBuilder getActivityBuilderOnSameTask() { - return getActivityBuilderWithoutTask().setTask(mTask); - } - private ActivityBuilder getActivityBuilderWithoutTask() { return new ActivityBuilder(mAtm) .setComponent(ComponentName.createRelative(mContext, diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index c8aa61c0316d..a7fe0cb0940c 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2127,7 +2127,6 @@ public class TelephonyManager { * <p>On some devices, this settings activity may not exist. Callers should ensure that this * case is appropriately handled. */ - @FlaggedApi(Flags.FLAG_RESET_MOBILE_NETWORK_SETTINGS) @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS"; @@ -8779,13 +8778,14 @@ public class TelephonyManager { * Authentication error, no memory space available in EFMUK * * @throws UnsupportedOperationException If the device does not have - * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given + * authType. */ // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since // it's not public API. @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) - public String getIccAuthentication(int appType,@AuthType int authType, String data) { + public String getIccAuthentication(int appType, @AuthType int authType, String data) { return getIccAuthentication(getSubId(), appType, authType, data); } @@ -8810,10 +8810,14 @@ public class TelephonyManager { * Key freshness failure * Authentication error, no memory space available * Authentication error, no memory space available in EFMUK + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given + * authType. * @hide */ @UnsupportedAppUsage - public String getIccAuthentication(int subId, int appType,@AuthType int authType, String data) { + public String getIccAuthentication(int subId, int appType, @AuthType int authType, + String data) { try { IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) diff --git a/tests/BootImageProfileTest/Android.bp b/tests/BootImageProfileTest/Android.bp index 9fb5aa21f985..dbdc4b4407b7 100644 --- a/tests/BootImageProfileTest/Android.bp +++ b/tests/BootImageProfileTest/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_base_license"], + default_team: "trendy_team_art_mainline", } java_test_host { diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt index c77413b6a55a..c6855b4a97b4 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt @@ -59,6 +59,11 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : BOTTOM } + enum class AppProperty { + STANDARD, + NON_RESIZABLE + } + /** Wait for an app moved to desktop to finish its transition. */ private fun waitForAppToMoveToDesktop(wmHelper: WindowManagerStateHelper) { wmHelper diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt index 8d27c1d1dfd1..fd13d14074d4 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt @@ -128,6 +128,6 @@ constructor( } companion object { - private const val BOUNDS_OFFSET: Int = 100 + private const val BOUNDS_OFFSET: Int = 50 } } diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index f2e34257ef01..9ce8e807f612 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -352,7 +352,8 @@ android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity" android:theme="@style/CutoutShortEdges" android:label="SplitScreenPrimaryActivity" - android:exported="true"> + android:exported="true" + android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> @@ -363,7 +364,8 @@ android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity" android:theme="@style/CutoutShortEdges" android:label="SplitScreenSecondaryActivity" - android:exported="true"> + android:exported="true" + android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> diff --git a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt index 90dff47ab706..a1e165551b5b 100644 --- a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt +++ b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt @@ -24,17 +24,16 @@ import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import androidx.test.core.app.ApplicationProvider import com.android.server.testutils.any +import com.android.test.input.MockInputManagerRule import java.util.concurrent.Executor import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue import kotlin.test.fail -import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.anyInt import org.mockito.Mockito.doAnswer @@ -61,9 +60,8 @@ class InputDeviceBatteryListenerTest { private lateinit var context: Context private lateinit var inputManager: InputManager - @Mock - private lateinit var iInputManagerMock: IInputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession + @get:Rule + val inputManagerRule = MockInputManagerRule() @Before fun setUp() { @@ -72,7 +70,6 @@ class InputDeviceBatteryListenerTest { executor = HandlerExecutor(Handler(testLooper.looper)) registeredListener = null monitoredDevices.clear() - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -92,7 +89,7 @@ class InputDeviceBatteryListenerTest { monitoredDevices.add(deviceId) registeredListener = listener null - }.`when`(iInputManagerMock).registerBatteryListener(anyInt(), any()) + }.`when`(inputManagerRule.mock).registerBatteryListener(anyInt(), any()) // Handle battery listener being unregistered. doAnswer { @@ -108,14 +105,7 @@ class InputDeviceBatteryListenerTest { if (monitoredDevices.isEmpty()) { registeredListener = null } - }.`when`(iInputManagerMock).unregisterBatteryListener(anyInt(), any()) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + }.`when`(inputManagerRule.mock).unregisterBatteryListener(anyInt(), any()) } private fun notifyBatteryStateChanged( diff --git a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java index 080186e4a2c1..3fc9ce12e718 100644 --- a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java +++ b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java @@ -45,15 +45,14 @@ import android.view.InputDevice; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.test.input.MockInputManagerRule; + import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.Arrays; @@ -73,23 +72,22 @@ public class InputDeviceLightsManagerTest { private static final int DEVICE_ID = 1000; private static final int PLAYER_ID = 3; - @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + @Rule + public final MockInputManagerRule mInputManagerRule = new MockInputManagerRule(); private InputManager mInputManager; - @Mock private IInputManager mIInputManagerMock; private InputManagerGlobal.TestSession mInputManagerGlobalSession; @Before public void setUp() throws Exception { final Context context = spy( new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())); - when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); + when(mInputManagerRule.getMock().getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); - when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn( + when(mInputManagerRule.getMock().getInputDevice(eq(DEVICE_ID))).thenReturn( createInputDevice(DEVICE_ID)); - mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mInputManager = new InputManager(context); when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager); @@ -102,7 +100,7 @@ public class InputDeviceLightsManagerTest { lightStatesById.put(lightIds[i], lightStates[i]); } return null; - }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID), + }).when(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), any(int[].class), any(LightState[].class), any(IBinder.class)); doAnswer(invocation -> { @@ -111,7 +109,7 @@ public class InputDeviceLightsManagerTest { return lightStatesById.get(lightId); } return new LightState(0); - }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt()); + }).when(mInputManagerRule.getMock()).getLightState(eq(DEVICE_ID), anyInt()); } @After @@ -130,7 +128,7 @@ public class InputDeviceLightsManagerTest { private void mockLights(Light[] lights) throws Exception { // Mock the Lights returned form InputManagerService - when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn( + when(mInputManagerRule.getMock().getLights(eq(DEVICE_ID))).thenReturn( new ArrayList(Arrays.asList(lights))); } @@ -151,7 +149,7 @@ public class InputDeviceLightsManagerTest { LightsManager lightsManager = device.getLightsManager(); List<Light> lights = lightsManager.getLights(); - verify(mIInputManagerMock).getLights(eq(DEVICE_ID)); + verify(mInputManagerRule.getMock()).getLights(eq(DEVICE_ID)); assertEquals(lights, Arrays.asList(mockedLights)); } @@ -185,9 +183,9 @@ public class InputDeviceLightsManagerTest { .build()); IBinder token = session.getToken(); - verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID), + verify(mInputManagerRule.getMock()).openLightSession(eq(DEVICE_ID), any(String.class), eq(token)); - verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}), + verify(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}), eq(states), eq(token)); // Then all 3 should turn on. @@ -204,7 +202,7 @@ public class InputDeviceLightsManagerTest { // close session session.close(); - verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token)); + verify(mInputManagerRule.getMock()).closeLightSession(eq(DEVICE_ID), eq(token)); } @Test @@ -232,9 +230,9 @@ public class InputDeviceLightsManagerTest { .build()); IBinder token = session.getToken(); - verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID), + verify(mInputManagerRule.getMock()).openLightSession(eq(DEVICE_ID), any(String.class), eq(token)); - verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}), + verify(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), eq(new int[]{1}), eq(states), eq(token)); // Verify the light state @@ -245,7 +243,7 @@ public class InputDeviceLightsManagerTest { // close session session.close(); - verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token)); + verify(mInputManagerRule.getMock()).closeLightSession(eq(DEVICE_ID), eq(token)); } @Test diff --git a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java index 0e3c200699d2..3057f5ddb540 100644 --- a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java +++ b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java @@ -41,16 +41,13 @@ import android.view.InputDevice; import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.annotations.GuardedBy; +import com.android.test.input.MockInputManagerRule; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.junit.MockitoRule; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -70,43 +67,34 @@ public class InputDeviceSensorManagerTest { private static final int DEVICE_ID = 1000; - @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + @Rule + public final MockInputManagerRule mInputManagerRule = new MockInputManagerRule(); private InputManager mInputManager; private IInputSensorEventListener mIInputSensorEventListener; private final Object mLock = new Object(); - @Mock private IInputManager mIInputManagerMock; - private InputManagerGlobal.TestSession mInputManagerGlobalSession; - @Before public void setUp() throws Exception { final Context context = spy( new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())); - mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mInputManager = new InputManager(context); when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager); - when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); + when(mInputManagerRule.getMock().getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); - when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn( + when(mInputManagerRule.getMock().getInputDevice(eq(DEVICE_ID))).thenReturn( createInputDeviceWithSensor(DEVICE_ID)); - when(mIInputManagerMock.getSensorList(eq(DEVICE_ID))).thenReturn(new InputSensorInfo[] { - createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER), - createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)}); + when(mInputManagerRule.getMock().getSensorList(eq(DEVICE_ID))).thenReturn( + new InputSensorInfo[]{ + createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER), + createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)}); - when(mIInputManagerMock.enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt())) + when(mInputManagerRule.getMock().enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt())) .thenReturn(true); - when(mIInputManagerMock.registerSensorListener(any())).thenReturn(true); - } - - @After - public void tearDown() { - if (mInputManagerGlobalSession != null) { - mInputManagerGlobalSession.close(); - } + when(mInputManagerRule.getMock().registerSensorListener(any())).thenReturn(true); } private class InputTestSensorEventListener implements SensorEventListener { @@ -175,13 +163,13 @@ public class InputDeviceSensorManagerTest { SensorManager sensorManager = device.getSensorManager(); List<Sensor> accelList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER); - verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID)); + verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID)); assertEquals(1, accelList.size()); assertEquals(DEVICE_ID, accelList.get(0).getId()); assertEquals(Sensor.TYPE_ACCELEROMETER, accelList.get(0).getType()); List<Sensor> gyroList = sensorManager.getSensorList(Sensor.TYPE_GYROSCOPE); - verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID)); + verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID)); assertEquals(1, gyroList.size()); assertEquals(DEVICE_ID, gyroList.get(0).getId()); assertEquals(Sensor.TYPE_GYROSCOPE, gyroList.get(0).getType()); @@ -197,11 +185,11 @@ public class InputDeviceSensorManagerTest { List<Sensor> gameRotationList = sensorManager.getSensorList( Sensor.TYPE_GAME_ROTATION_VECTOR); - verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID)); + verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID)); assertEquals(0, gameRotationList.size()); List<Sensor> gravityList = sensorManager.getSensorList(Sensor.TYPE_GRAVITY); - verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID)); + verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID)); assertEquals(0, gravityList.size()); } @@ -218,13 +206,13 @@ public class InputDeviceSensorManagerTest { mIInputSensorEventListener = invocation.getArgument(0); assertNotNull(mIInputSensorEventListener); return true; - }).when(mIInputManagerMock).registerSensorListener(any()); + }).when(mInputManagerRule.getMock()).registerSensorListener(any()); InputTestSensorEventListener listener = new InputTestSensorEventListener(); assertTrue(sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)); - verify(mIInputManagerMock).registerSensorListener(any()); - verify(mIInputManagerMock).enableSensor(eq(DEVICE_ID), eq(sensor.getType()), + verify(mInputManagerRule.getMock()).registerSensorListener(any()); + verify(mInputManagerRule.getMock()).enableSensor(eq(DEVICE_ID), eq(sensor.getType()), anyInt(), anyInt()); float[] values = new float[] {0.12f, 9.8f, 0.2f}; @@ -240,7 +228,7 @@ public class InputDeviceSensorManagerTest { } sensorManager.unregisterListener(listener); - verify(mIInputManagerMock).disableSensor(eq(DEVICE_ID), eq(sensor.getType())); + verify(mInputManagerRule.getMock()).disableSensor(eq(DEVICE_ID), eq(sensor.getType())); } } diff --git a/tests/Input/src/android/hardware/input/InputManagerTest.kt b/tests/Input/src/android/hardware/input/InputManagerTest.kt index 152dde94f006..4c6bb849155c 100644 --- a/tests/Input/src/android/hardware/input/InputManagerTest.kt +++ b/tests/Input/src/android/hardware/input/InputManagerTest.kt @@ -23,18 +23,16 @@ import android.view.Display import android.view.DisplayInfo import android.view.InputDevice import androidx.test.core.app.ApplicationProvider -import org.junit.After -import org.junit.Assert.assertNotNull +import com.android.test.input.MockInputManagerRule import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.eq import org.mockito.Mockito.`when` -import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoJUnitRunner /** @@ -54,35 +52,23 @@ class InputManagerTest { } @get:Rule - val rule = MockitoJUnit.rule()!! + val inputManagerRule = MockInputManagerRule() private lateinit var devicesChangedListener: IInputDevicesChangedListener private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>() private lateinit var context: Context private lateinit var inputManager: InputManager - @Mock - private lateinit var iInputManager: IInputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession - @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) inputManager = InputManager(context) `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager) - `when`(iInputManager.inputDeviceIds).then { + `when`(inputManagerRule.mock.inputDeviceIds).then { deviceGenerationMap.keys.toIntArray() } } - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } - } - private fun notifyDeviceChanged( deviceId: Int, associatedDisplayId: Int, @@ -92,7 +78,7 @@ class InputManagerTest { ?: throw IllegalArgumentException("Device $deviceId was never added!") deviceGenerationMap[deviceId] = generation - `when`(iInputManager.getInputDevice(deviceId)) + `when`(inputManagerRule.mock.getInputDevice(deviceId)) .thenReturn(createInputDevice(deviceId, associatedDisplayId, usiVersion, generation)) val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) } if (::devicesChangedListener.isInitialized) { @@ -125,7 +111,7 @@ class InputManagerTest { fun testUsiVersionFallBackToDisplayConfig() { addInputDevice(DEVICE_ID, Display.DEFAULT_DISPLAY, null) - `when`(iInputManager.getHostUsiVersionFromDisplayConfig(eq(42))) + `when`(inputManagerRule.mock.getHostUsiVersionFromDisplayConfig(eq(42))) .thenReturn(HostUsiVersion(9, 8)) val usiVersion = inputManager.getHostUsiVersion(createDisplay(42)) assertEquals(HostUsiVersion(9, 8), usiVersion) diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt index 072341dcefae..e99c81493394 100644 --- a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt +++ b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt @@ -18,20 +18,17 @@ package android.hardware.input import android.content.Context import android.content.ContextWrapper -import android.os.Handler import android.os.IBinder -import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.platform.test.flag.junit.SetFlagsRule import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider import com.android.server.testutils.any -import org.junit.After +import com.android.test.input.MockInputManagerRule import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.doAnswer import org.mockito.Mockito.`when` @@ -69,20 +66,16 @@ class KeyGestureEventHandlerTest { @get:Rule val rule = SetFlagsRule() + @get:Rule + val inputManagerRule = MockInputManagerRule() - private val testLooper = TestLooper() private var registeredListener: IKeyGestureHandler? = null private lateinit var context: Context private lateinit var inputManager: InputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession - - @Mock - private lateinit var iInputManagerMock: IInputManager @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -97,7 +90,7 @@ class KeyGestureEventHandlerTest { } registeredListener = listener null - }.`when`(iInputManagerMock).registerKeyGestureHandler(any()) + }.`when`(inputManagerRule.mock).registerKeyGestureHandler(any()) // Handle key gesture handler being unregistered. doAnswer { @@ -108,14 +101,7 @@ class KeyGestureEventHandlerTest { } registeredListener = null null - }.`when`(iInputManagerMock).unregisterKeyGestureHandler(any()) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + }.`when`(inputManagerRule.mock).unregisterKeyGestureHandler(any()) } private fun handleKeyGestureEvent(event: KeyGestureEvent) { diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt index ca9de6000a5a..cf0bfcc4f6df 100644 --- a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt +++ b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt @@ -26,20 +26,19 @@ import android.platform.test.flag.junit.SetFlagsRule import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider import com.android.server.testutils.any -import org.junit.After +import com.android.test.input.MockInputManagerRule +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.fail import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.doAnswer import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnitRunner -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.fail /** * Tests for [InputManager.KeyGestureEventListener]. @@ -63,21 +62,18 @@ class KeyGestureEventListenerTest { @get:Rule val rule = SetFlagsRule() + @get:Rule + val inputManagerRule = MockInputManagerRule() private val testLooper = TestLooper() private val executor = HandlerExecutor(Handler(testLooper.looper)) private var registeredListener: IKeyGestureEventListener? = null private lateinit var context: Context private lateinit var inputManager: InputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession - - @Mock - private lateinit var iInputManagerMock: IInputManager @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -92,7 +88,7 @@ class KeyGestureEventListenerTest { } registeredListener = listener null - }.`when`(iInputManagerMock).registerKeyGestureEventListener(any()) + }.`when`(inputManagerRule.mock).registerKeyGestureEventListener(any()) // Handle key gesture event listener being unregistered. doAnswer { @@ -103,14 +99,7 @@ class KeyGestureEventListenerTest { } registeredListener = null null - }.`when`(iInputManagerMock).unregisterKeyGestureEventListener(any()) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + }.`when`(inputManagerRule.mock).unregisterKeyGestureEventListener(any()) } private fun notifyKeyGestureEvent(event: KeyGestureEvent) { diff --git a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt index 23135b2550b0..d25dee1d402c 100644 --- a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt +++ b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt @@ -24,22 +24,20 @@ import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import androidx.test.core.app.ApplicationProvider import com.android.server.testutils.any -import org.junit.After +import com.android.test.input.MockInputManagerRule +import java.util.concurrent.Executor +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.fail import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.doAnswer import org.mockito.Mockito.`when` -import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoJUnitRunner -import java.util.concurrent.Executor -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.fail /** * Tests for [InputManager.KeyboardBacklightListener]. @@ -50,23 +48,19 @@ import kotlin.test.fail @Presubmit @RunWith(MockitoJUnitRunner::class) class KeyboardBacklightListenerTest { + @get:Rule - val rule = MockitoJUnit.rule()!! + val inputManagerRule = MockInputManagerRule() private lateinit var testLooper: TestLooper private var registeredListener: IKeyboardBacklightListener? = null private lateinit var executor: Executor private lateinit var context: Context private lateinit var inputManager: InputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession - - @Mock - private lateinit var iInputManagerMock: IInputManager @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) testLooper = TestLooper() executor = HandlerExecutor(Handler(testLooper.looper)) registeredListener = null @@ -84,7 +78,7 @@ class KeyboardBacklightListenerTest { } registeredListener = listener null - }.`when`(iInputManagerMock).registerKeyboardBacklightListener(any()) + }.`when`(inputManagerRule.mock).registerKeyboardBacklightListener(any()) // Handle keyboard backlight listener being unregistered. doAnswer { @@ -95,14 +89,7 @@ class KeyboardBacklightListenerTest { } registeredListener = null null - }.`when`(iInputManagerMock).unregisterKeyboardBacklightListener(any()) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + }.`when`(inputManagerRule.mock).unregisterKeyboardBacklightListener(any()) } private fun notifyKeyboardBacklightChanged( diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt index bcd56ad0c669..1c2a0538e552 100644 --- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt +++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt @@ -27,21 +27,20 @@ import android.platform.test.flag.junit.SetFlagsRule import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider import com.android.server.testutils.any -import org.junit.After +import com.android.test.input.MockInputManagerRule +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue +import kotlin.test.fail import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.doAnswer import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnitRunner -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.assertTrue -import kotlin.test.fail /** * Tests for [InputManager.StickyModifierStateListener]. @@ -59,21 +58,18 @@ class StickyModifierStateListenerTest { @get:Rule val rule = SetFlagsRule() + @get:Rule + val inputManagerRule = MockInputManagerRule() private val testLooper = TestLooper() private val executor = HandlerExecutor(Handler(testLooper.looper)) private var registeredListener: IStickyModifierStateListener? = null private lateinit var context: Context private lateinit var inputManager: InputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession - - @Mock - private lateinit var iInputManagerMock: IInputManager @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -88,7 +84,7 @@ class StickyModifierStateListenerTest { } registeredListener = listener null - }.`when`(iInputManagerMock).registerStickyModifierStateListener(any()) + }.`when`(inputManagerRule.mock).registerStickyModifierStateListener(any()) // Handle sticky modifier state listener being unregistered. doAnswer { @@ -99,14 +95,7 @@ class StickyModifierStateListenerTest { } registeredListener = null null - }.`when`(iInputManagerMock).unregisterStickyModifierStateListener(any()) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + }.`when`(inputManagerRule.mock).unregisterStickyModifierStateListener(any()) } private fun notifyStickyModifierStateChanged(modifierState: Int, lockedModifierState: Int) { diff --git a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt index f2724e605553..044f11d6904c 100644 --- a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt +++ b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt @@ -27,7 +27,6 @@ import android.hardware.input.HostUsiVersion import android.hardware.input.IInputDeviceBatteryListener import android.hardware.input.IInputDeviceBatteryState import android.hardware.input.IInputDevicesChangedListener -import android.hardware.input.IInputManager import android.hardware.input.InputManager import android.hardware.input.InputManagerGlobal import android.os.Binder @@ -42,13 +41,13 @@ import com.android.server.input.BatteryController.BluetoothBatteryManager.Blueto import com.android.server.input.BatteryController.POLLING_PERIOD_MILLIS import com.android.server.input.BatteryController.UEventBatteryListener import com.android.server.input.BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS +import com.android.test.input.MockInputManagerRule import org.hamcrest.Description import org.hamcrest.Matcher import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers import org.hamcrest.TypeSafeMatcher import org.hamcrest.core.IsEqual.equalTo -import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -184,12 +183,12 @@ class BatteryControllerTests { @get:Rule val rule = MockitoJUnit.rule()!! + @get:Rule + val inputManagerRule = MockInputManagerRule() @Mock private lateinit var native: NativeInputManagerService @Mock - private lateinit var iInputManager: IInputManager - @Mock private lateinit var uEventManager: UEventManager @Mock private lateinit var bluetoothBatteryManager: BluetoothBatteryManager @@ -205,10 +204,9 @@ class BatteryControllerTests { fun setup() { context = TestableContext(ApplicationProvider.getApplicationContext()) testLooper = TestLooper() - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) context.addMockSystemService(InputManager::class.java, inputManager) - `when`(iInputManager.inputDeviceIds).then { + `when`(inputManagerRule.mock.inputDeviceIds).then { deviceGenerationMap.keys.toIntArray() } addInputDevice(DEVICE_ID) @@ -218,18 +216,11 @@ class BatteryControllerTests { bluetoothBatteryManager) batteryController.systemRunning() val listenerCaptor = ArgumentCaptor.forClass(IInputDevicesChangedListener::class.java) - verify(iInputManager).registerInputDevicesChangedListener(listenerCaptor.capture()) + verify(inputManagerRule.mock).registerInputDevicesChangedListener(listenerCaptor.capture()) devicesChangedListener = listenerCaptor.value testLooper.dispatchAll() } - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } - } - private fun notifyDeviceChanged( deviceId: Int, hasBattery: Boolean = true, @@ -239,7 +230,7 @@ class BatteryControllerTests { ?: throw IllegalArgumentException("Device $deviceId was never added!") deviceGenerationMap[deviceId] = generation - `when`(iInputManager.getInputDevice(deviceId)) + `when`(inputManagerRule.mock.getInputDevice(deviceId)) .thenReturn(createInputDevice(deviceId, hasBattery, supportsUsi, generation)) val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) } if (::devicesChangedListener.isInitialized) { @@ -657,9 +648,9 @@ class BatteryControllerTests { @Test fun testRegisterBluetoothListenerForMonitoredBluetoothDevices() { - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") - `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) .thenReturn("11:22:33:44:55:66") addInputDevice(BT_DEVICE_ID) testLooper.dispatchNext() @@ -686,7 +677,7 @@ class BatteryControllerTests { batteryController.unregisterBatteryListener(BT_DEVICE_ID, listener, PID) verify(bluetoothBatteryManager, never()).removeBatteryListener(any()) - `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) .thenReturn(null) notifyDeviceChanged(SECOND_BT_DEVICE_ID) testLooper.dispatchNext() @@ -695,7 +686,7 @@ class BatteryControllerTests { @Test fun testNotifiesBluetoothBatteryChanges() { - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21) addInputDevice(BT_DEVICE_ID) @@ -716,7 +707,7 @@ class BatteryControllerTests { @Test fun testBluetoothBatteryIsPrioritized() { `when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device") - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21) `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98) @@ -745,7 +736,7 @@ class BatteryControllerTests { @Test fun testFallBackToNativeBatteryStateWhenBluetoothStateInvalid() { `when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device") - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21) `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98) @@ -776,9 +767,9 @@ class BatteryControllerTests { @Test fun testRegisterBluetoothMetadataListenerForMonitoredBluetoothDevices() { - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") - `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) .thenReturn("11:22:33:44:55:66") addInputDevice(BT_DEVICE_ID) testLooper.dispatchNext() @@ -811,7 +802,7 @@ class BatteryControllerTests { verify(bluetoothBatteryManager) .removeMetadataListener("AA:BB:CC:DD:EE:FF", metadataListener1.value) - `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) .thenReturn(null) notifyDeviceChanged(SECOND_BT_DEVICE_ID) testLooper.dispatchNext() @@ -821,7 +812,7 @@ class BatteryControllerTests { @Test fun testNotifiesBluetoothMetadataBatteryChanges() { - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF", BluetoothDevice.METADATA_MAIN_BATTERY)) @@ -861,7 +852,7 @@ class BatteryControllerTests { @Test fun testBluetoothMetadataBatteryIsPrioritized() { - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21) `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF", diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt index 351ec4635977..927958eb62cc 100644 --- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt +++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt @@ -93,14 +93,12 @@ class InputManagerServiceTests { ) } - @JvmField - @Rule + @get:Rule val extendedMockitoRule = ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java) .mockStatic(PermissionChecker::class.java).build()!! - @JvmField - @Rule + @get:Rule val setFlagsRule = SetFlagsRule() @get:Rule diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 4ae06a4f9812..7526737f60bf 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -46,6 +46,7 @@ import com.android.internal.util.FrameworkStatsLog import com.android.modules.utils.testing.ExtendedMockitoRule import junitparams.JUnitParamsRunner import junitparams.Parameters +import org.junit.After import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -128,6 +129,13 @@ class KeyGestureControllerTests { currentPid = Process.myPid() } + @After + fun teardown() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + } + private fun setupBehaviors() { Mockito.`when`( resources.getBoolean( diff --git a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt index f74fd723d540..4f4c97bef4c0 100644 --- a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt +++ b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt @@ -18,16 +18,18 @@ package com.android.server.input import android.content.Context import android.content.ContextWrapper -import android.hardware.input.IInputManager import android.hardware.input.InputManager -import android.hardware.input.InputManagerGlobal import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.provider.Settings import android.view.InputDevice import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider -import org.junit.After +import com.android.test.input.MockInputManagerRule +import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Rule @@ -35,10 +37,6 @@ import org.junit.Test import org.mockito.Mock import org.mockito.Mockito import org.mockito.junit.MockitoJUnit -import java.io.FileNotFoundException -import java.io.FileOutputStream -import java.io.IOException -import java.io.InputStream private fun createKeyboard(deviceId: Int): InputDevice = InputDevice.Builder() @@ -73,15 +71,15 @@ class KeyRemapperTests { @get:Rule val rule = MockitoJUnit.rule()!! - @Mock - private lateinit var iInputManager: IInputManager + @get:Rule + val inputManagerRule = MockInputManagerRule() + @Mock private lateinit var native: NativeInputManagerService private lateinit var mKeyRemapper: KeyRemapper private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Before fun setup() { @@ -104,25 +102,17 @@ class KeyRemapperTests { dataStore, testLooper.looper ) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) - Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) } @Test fun testKeyRemapping_whenRemappingEnabled() { ModifierRemappingFlag(true).use { val keyboard = createKeyboard(DEVICE_ID) - Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard) + Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboard) for (i in REMAPPABLE_KEYS.indices) { val fromKeyCode = REMAPPABLE_KEYS[i] @@ -160,7 +150,7 @@ class KeyRemapperTests { fun testKeyRemapping_whenRemappingDisabled() { ModifierRemappingFlag(false).use { val keyboard = createKeyboard(DEVICE_ID) - Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard) + Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboard) mKeyRemapper.remapKey(REMAPPABLE_KEYS[0], REMAPPABLE_KEYS[1]) testLooper.dispatchAll() diff --git a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt index 59aa96c46336..58fb4e1ed103 100644 --- a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt @@ -20,11 +20,9 @@ import android.animation.ValueAnimator import android.content.Context import android.content.ContextWrapper import android.graphics.Color -import android.hardware.input.IInputManager import android.hardware.input.IKeyboardBacklightListener import android.hardware.input.IKeyboardBacklightState import android.hardware.input.InputManager -import android.hardware.input.InputManagerGlobal import android.hardware.lights.Light import android.os.UEventObserver import android.os.test.TestLooper @@ -35,7 +33,11 @@ import androidx.test.core.app.ApplicationProvider import com.android.server.input.KeyboardBacklightController.DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANGE_STEPS import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS -import org.junit.After +import com.android.test.input.MockInputManagerRule +import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotEquals @@ -52,10 +54,6 @@ import org.mockito.Mockito.eq import org.mockito.Mockito.spy import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit -import java.io.FileNotFoundException -import java.io.FileOutputStream -import java.io.IOException -import java.io.InputStream private fun createKeyboard(deviceId: Int): InputDevice = InputDevice.Builder() @@ -100,10 +98,10 @@ class KeyboardBacklightControllerTests { @get:Rule val rule = MockitoJUnit.rule()!! + @get:Rule + val inputManagerRule = MockInputManagerRule() @Mock - private lateinit var iInputManager: IInputManager - @Mock private lateinit var native: NativeInputManagerService @Mock private lateinit var uEventManager: UEventManager @@ -111,7 +109,6 @@ class KeyboardBacklightControllerTests { private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession private var lightColorMap: HashMap<Int, Int> = HashMap() private var lastBacklightState: KeyboardBacklightState? = null private var sysfsNodeChanges = 0 @@ -134,10 +131,9 @@ class KeyboardBacklightControllerTests { testLooper = TestLooper() keyboardBacklightController = KeyboardBacklightController(context, native, dataStore, testLooper.looper, FakeAnimatorFactory(), uEventManager) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager) - `when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) + `when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) `when`(native.setLightColor(anyInt(), anyInt(), anyInt())).then { val args = it.arguments lightColorMap.put(args[1] as Int, args[2] as Int) @@ -152,13 +148,6 @@ class KeyboardBacklightControllerTests { } } - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } - } - @Test fun testKeyboardBacklightIncrementDecrement() { KeyboardBacklightFlags( @@ -168,8 +157,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, @@ -186,8 +176,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithoutBacklight = createKeyboard(DEVICE_ID) val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithoutBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) incrementKeyboardBacklight(DEVICE_ID) @@ -205,8 +196,9 @@ class KeyboardBacklightControllerTests { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn( + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn( listOf( keyboardBacklight, keyboardInputLight @@ -230,8 +222,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) for (level in 1 until DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size) { dataStore.setKeyboardBacklightBrightness( @@ -263,7 +256,8 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID, @@ -278,7 +272,7 @@ class KeyboardBacklightControllerTests { lightColorMap.isEmpty() ) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceChanged(DEVICE_ID) keyboardBacklightController.notifyUserActivity() testLooper.dispatchNext() @@ -300,8 +294,9 @@ class KeyboardBacklightControllerTests { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) val maxLevel = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size - 1 - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) // Register backlight listener @@ -352,8 +347,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID, @@ -388,8 +384,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID, @@ -482,8 +479,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) incrementKeyboardBacklight(DEVICE_ID) @@ -511,8 +509,9 @@ class KeyboardBacklightControllerTests { val suggestedLevels = intArrayOf(0, 22, 63, 135, 196, 255) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, @@ -531,8 +530,9 @@ class KeyboardBacklightControllerTests { val suggestedLevels = IntArray(MAX_BRIGHTNESS_CHANGE_STEPS + 1) { 10 * (it + 1) } val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, @@ -551,8 +551,9 @@ class KeyboardBacklightControllerTests { val suggestedLevels = intArrayOf(22, 63, 135, 196) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) // Framework will add the lowest and maximum levels if not provided via config @@ -572,8 +573,10 @@ class KeyboardBacklightControllerTests { val suggestedLevels = intArrayOf(22, 63, 135, 400, 196, 1000) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)) + .thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) // Framework will drop out of bound levels in the config @@ -591,8 +594,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, @@ -619,8 +623,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) incrementKeyboardBacklight(DEVICE_ID) @@ -642,8 +647,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) sendAmbientBacklightValue(1) assertEquals( @@ -671,8 +677,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) sendAmbientBacklightValue(254) assertEquals( @@ -701,8 +708,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) incrementKeyboardBacklight(DEVICE_ID) assertEquals( diff --git a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt index c073c7aae678..ff8a9ba94353 100644 --- a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt @@ -23,9 +23,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.content.pm.ServiceInfo -import android.hardware.input.IInputManager import android.hardware.input.InputManager -import android.hardware.input.InputManagerGlobal import android.hardware.input.KeyGlyphMap.KeyCombination import android.os.Bundle import android.os.test.TestLooper @@ -36,8 +34,8 @@ import android.view.InputDevice import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider import com.android.hardware.input.Flags +import com.android.test.input.MockInputManagerRule import com.android.test.input.R -import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull @@ -65,30 +63,24 @@ class KeyboardGlyphManagerTests { const val RECEIVER_NAME = "DummyReceiver" } - @JvmField - @Rule(order = 0) + @get:Rule val setFlagsRule = SetFlagsRule() - - @JvmField - @Rule(order = 1) + @get:Rule val mockitoRule = MockitoJUnit.rule()!! + @get:Rule + val inputManagerRule = MockInputManagerRule() @Mock private lateinit var packageManager: PackageManager - @Mock - private lateinit var iInputManager: IInputManager - private lateinit var keyboardGlyphManager: KeyboardGlyphManager private lateinit var context: Context private lateinit var testLooper: TestLooper - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession private lateinit var keyboardDevice: InputDevice @Before fun setup() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) testLooper = TestLooper() keyboardGlyphManager = KeyboardGlyphManager(context, testLooper.looper) @@ -98,21 +90,14 @@ class KeyboardGlyphManagerTests { testLooper.dispatchAll() } - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } - } - private fun setupInputDevices() { val inputManager = InputManager(context) Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) keyboardDevice = createKeyboard(DEVICE_ID, VENDOR_ID, PRODUCT_ID, 0, "", "") - Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) - Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice) + Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) + Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice) } private fun setupBroadcastReceiver() { diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt index 301c0e6a159f..d6654cceb458 100644 --- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -24,11 +24,10 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.content.pm.ServiceInfo -import android.hardware.input.KeyboardLayoutSelectionResult -import android.hardware.input.IInputManager import android.hardware.input.InputManager import android.hardware.input.InputManagerGlobal import android.hardware.input.KeyboardLayout +import android.hardware.input.KeyboardLayoutSelectionResult import android.icu.util.ULocale import android.os.Bundle import android.os.test.TestLooper @@ -42,8 +41,12 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.internal.os.KeyboardConfiguredProto import com.android.internal.util.FrameworkStatsLog import com.android.modules.utils.testing.ExtendedMockitoRule +import com.android.test.input.MockInputManagerRule import com.android.test.input.R -import org.junit.After +import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals import org.junit.Assert.assertTrue @@ -53,10 +56,6 @@ import org.junit.Test import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito -import java.io.FileNotFoundException -import java.io.FileOutputStream -import java.io.IOException -import java.io.InputStream fun createKeyboard( deviceId: Int, @@ -120,8 +119,8 @@ class KeyboardLayoutManagerTests { val extendedMockitoRule = ExtendedMockitoRule.Builder(this) .mockStatic(FrameworkStatsLog::class.java).build()!! - @Mock - private lateinit var iInputManager: IInputManager + @get:Rule + val inputManagerRule = MockInputManagerRule() @Mock private lateinit var native: NativeInputManagerService @@ -148,7 +147,6 @@ class KeyboardLayoutManagerTests { @Before fun setup() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) dataStore = PersistentDataStore(object : PersistentDataStore.Injector() { override fun openRead(): InputStream? { throw FileNotFoundException() @@ -171,13 +169,6 @@ class KeyboardLayoutManagerTests { setupIme() } - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } - } - private fun setupInputDevices() { val inputManager = InputManager(context) Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) @@ -191,19 +182,19 @@ class KeyboardLayoutManagerTests { DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "dvorak") englishQwertyKeyboardDevice = createKeyboard(ENGLISH_QWERTY_DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "qwerty") - Mockito.`when`(iInputManager.inputDeviceIds) + Mockito.`when`(inputManagerRule.mock.inputDeviceIds) .thenReturn(intArrayOf( DEVICE_ID, VENDOR_SPECIFIC_DEVICE_ID, ENGLISH_DVORAK_DEVICE_ID, ENGLISH_QWERTY_DEVICE_ID )) - Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice) - Mockito.`when`(iInputManager.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID)) + Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice) + Mockito.`when`(inputManagerRule.mock.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID)) .thenReturn(vendorSpecificKeyboardDevice) - Mockito.`when`(iInputManager.getInputDevice(ENGLISH_DVORAK_DEVICE_ID)) + Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_DVORAK_DEVICE_ID)) .thenReturn(englishDvorakKeyboardDevice) - Mockito.`when`(iInputManager.getInputDevice(ENGLISH_QWERTY_DEVICE_ID)) + Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_QWERTY_DEVICE_ID)) .thenReturn(englishQwertyKeyboardDevice) } diff --git a/tests/Input/src/com/android/test/input/MockInputManagerRule.kt b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt new file mode 100644 index 000000000000..cef985600c40 --- /dev/null +++ b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test.input + +import android.hardware.input.IInputManager +import android.hardware.input.InputManagerGlobal +import org.junit.rules.ExternalResource +import org.mockito.Mockito + +/** + * A test rule that temporarily replaces the [IInputManager] connection to the server with a mock + * to be used for testing. + */ +class MockInputManagerRule : ExternalResource() { + + private lateinit var session: InputManagerGlobal.TestSession + + val mock: IInputManager = Mockito.mock(IInputManager::class.java) + + override fun before() { + session = InputManagerGlobal.createTestSession(mock) + } + + override fun after() { + if (this::session.isInitialized) { + session.close() + } + } +} diff --git a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt index 9f4df90422eb..c61a25021949 100644 --- a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt +++ b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt @@ -39,7 +39,6 @@ import com.android.cts.input.inputeventmatchers.withSource import junit.framework.Assert.fail import org.hamcrest.Matchers.allOf import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.TestName @@ -108,7 +107,6 @@ class UinputRecordingIntegrationTests { parser = InputJsonParser(instrumentation.context) } - @Ignore("b/366602644") @Test fun testEvemuRecording() { VirtualDisplayActivityScenario.AutoClose<CaptureEventActivity>( diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java index 28d7b42764c4..d78ced161eaf 100644 --- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java @@ -121,4 +121,12 @@ public class ProtoLogViewerConfigReaderTest { assertNull(mConfig.getViewerString(4)); assertNull(mConfig.getViewerString(5)); } + + @Test + public void loadUnloadAndReloadViewerConfig() { + loadViewerConfig(); + unloadViewerConfig(); + loadViewerConfig(); + unloadViewerConfig(); + } } diff --git a/tests/testables/src/android/testing/TestWithLooperRule.java b/tests/testables/src/android/testing/TestWithLooperRule.java index 10df17f991d3..6a8e142e2314 100644 --- a/tests/testables/src/android/testing/TestWithLooperRule.java +++ b/tests/testables/src/android/testing/TestWithLooperRule.java @@ -100,6 +100,9 @@ public class TestWithLooperRule implements MethodRule { case "ExpectException": next = this.getNextStatement(next, "next"); break; + case "UiThreadStatement": + next = this.getNextStatement(next, "base"); + break; default: throw new Exception( String.format("Unexpected Statement received: [%s]", diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp index 37b1687fd3f1..5983cf165839 100644 --- a/tools/aapt2/SdkConstants.cpp +++ b/tools/aapt2/SdkConstants.cpp @@ -28,8 +28,19 @@ using namespace std::literals; namespace aapt { static constexpr ApiVersion sDevelopmentSdkLevel = 10000; + +// clang-format off static constexpr StringPiece sDevelopmentSdkCodeNames[] = { - "Q"sv, "R"sv, "S"sv, "Sv2"sv, "Tiramisu"sv, "UpsideDownCake"sv, "VanillaIceCream"sv}; + "Q"sv, + "R"sv, + "S"sv, + "Sv2"sv, + "Tiramisu"sv, + "UpsideDownCake"sv, + "VanillaIceCream"sv, + "Baklava"sv, +}; +// clang-format on static constexpr auto sPrivacySandboxSuffix = "PrivacySandbox"sv; diff --git a/tools/hoststubgen/.gitignore b/tools/hoststubgen/.gitignore deleted file mode 100644 index 6453bdef8cee..000000000000 --- a/tools/hoststubgen/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -out/ -*-out/ -*.log diff --git a/tools/hoststubgen/OWNERS b/tools/hoststubgen/OWNERS deleted file mode 100644 index 3d8888d83cf4..000000000000 --- a/tools/hoststubgen/OWNERS +++ /dev/null @@ -1 +0,0 @@ -file:platform/frameworks/base:/ravenwood/OWNERS diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING deleted file mode 100644 index 856e6eefba15..000000000000 --- a/tools/hoststubgen/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "imports": [ - { - "path": "frameworks/base/ravenwood" - } - ] -} diff --git a/tools/hoststubgen/hoststubgen/.gitignore b/tools/hoststubgen/hoststubgen/.gitignore deleted file mode 100644 index 0f384074ed7f..000000000000 --- a/tools/hoststubgen/hoststubgen/.gitignore +++ /dev/null @@ -1 +0,0 @@ -framework-all-stub-out
\ No newline at end of file diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt index cba521e639cb..196b5e7c02ab 100644 --- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt @@ -22,8 +22,6 @@ import com.squareup.javapoet.JavaFile import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeSpec -import java.util.HashMap -import java.util.Map import javax.lang.model.element.Modifier /* @@ -52,7 +50,7 @@ import javax.lang.model.element.Modifier * public static boolean hasFeatureAutomotive(Context context); * public static boolean hasFeatureLeanback(Context context); * public static Boolean maybeHasFeature(String feature, int version); - * public static ArrayMap<String, FeatureInfo> getCompileTimeAvailableFeatures(); + * public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures(); * } * </pre> */ @@ -63,6 +61,7 @@ object SystemFeaturesGenerator { private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager") private val CONTEXT_CLASS = ClassName.get("android.content", "Context") private val FEATUREINFO_CLASS = ClassName.get("android.content.pm", "FeatureInfo") + private val ARRAYMAP_CLASS = ClassName.get("android.util", "ArrayMap") private val ASSUME_TRUE_CLASS = ClassName.get("com.android.aconfig.annotations", "AssumeTrueForR8") private val ASSUME_FALSE_CLASS = @@ -291,19 +290,19 @@ object SystemFeaturesGenerator { features: Collection<FeatureInfo>, ) { val methodBuilder = - MethodSpec.methodBuilder("getCompileTimeAvailableFeatures") + MethodSpec.methodBuilder("getReadOnlySystemEnabledFeatures") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addAnnotation(ClassName.get("android.annotation", "NonNull")) .addJavadoc("Gets features marked as available at compile-time, keyed by name." + "\n\n@hide") .returns(ParameterizedTypeName.get( - ClassName.get(Map::class.java), + ARRAYMAP_CLASS, ClassName.get(String::class.java), FEATUREINFO_CLASS)) val availableFeatures = features.filter { it.readonly && it.version != null } - methodBuilder.addStatement("Map<String, FeatureInfo> features = new \$T<>(\$L)", - HashMap::class.java, availableFeatures.size) + methodBuilder.addStatement("\$T<String, FeatureInfo> features = new \$T<>(\$L)", + ARRAYMAP_CLASS, ARRAYMAP_CLASS, availableFeatures.size) if (!availableFeatures.isEmpty()) { methodBuilder.addStatement("FeatureInfo fi = new FeatureInfo()") } diff --git a/tools/systemfeatures/tests/golden/RoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoFeatures.java.gen index edbfc4237547..ee97b26159de 100644 --- a/tools/systemfeatures/tests/golden/RoFeatures.java.gen +++ b/tools/systemfeatures/tests/golden/RoFeatures.java.gen @@ -13,10 +13,9 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; +import android.util.ArrayMap; import com.android.aconfig.annotations.AssumeFalseForR8; import com.android.aconfig.annotations.AssumeTrueForR8; -import java.util.HashMap; -import java.util.Map; /** * @hide @@ -94,8 +93,8 @@ public final class RoFeatures { * @hide */ @NonNull - public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() { - Map<String, FeatureInfo> features = new HashMap<>(2); + public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() { + ArrayMap<String, FeatureInfo> features = new ArrayMap<>(2); FeatureInfo fi = new FeatureInfo(); fi.name = PackageManager.FEATURE_WATCH; fi.version = 1; diff --git a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen index bf7a00679fa6..40c7db7ff1df 100644 --- a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen +++ b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen @@ -9,8 +9,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; -import java.util.HashMap; -import java.util.Map; +import android.util.ArrayMap; /** * @hide @@ -43,8 +42,8 @@ public final class RoNoFeatures { * @hide */ @NonNull - public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() { - Map<String, FeatureInfo> features = new HashMap<>(0); + public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() { + ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0); return features; } } diff --git a/tools/systemfeatures/tests/golden/RwFeatures.java.gen b/tools/systemfeatures/tests/golden/RwFeatures.java.gen index b20b228f9814..7bf89614b92d 100644 --- a/tools/systemfeatures/tests/golden/RwFeatures.java.gen +++ b/tools/systemfeatures/tests/golden/RwFeatures.java.gen @@ -12,8 +12,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; -import java.util.HashMap; -import java.util.Map; +import android.util.ArrayMap; /** * @hide @@ -73,8 +72,8 @@ public final class RwFeatures { * @hide */ @NonNull - public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() { - Map<String, FeatureInfo> features = new HashMap<>(0); + public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() { + ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0); return features; } } diff --git a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen index d91f5b62d8d4..eb7ec63f1d7d 100644 --- a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen +++ b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen @@ -7,8 +7,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.FeatureInfo; -import java.util.HashMap; -import java.util.Map; +import android.util.ArrayMap; /** * @hide @@ -32,8 +31,8 @@ public final class RwNoFeatures { * @hide */ @NonNull - public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() { - Map<String, FeatureInfo> features = new HashMap<>(0); + public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() { + ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0); return features; } } diff --git a/tools/systemfeatures/tests/src/ArrayMap.java b/tools/systemfeatures/tests/src/ArrayMap.java new file mode 100644 index 000000000000..a5ed9b088896 --- /dev/null +++ b/tools/systemfeatures/tests/src/ArrayMap.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import java.util.HashMap; + +/** Stub for testing. */ +public final class ArrayMap<K, V> extends HashMap<K, V> { + public ArrayMap(int capacity) { + super(capacity); + } +} diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java index 39f8fc44fe23..ed3f5c94ba79 100644 --- a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java +++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java @@ -60,7 +60,7 @@ public class SystemFeaturesGeneratorTest { assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull(); assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull(); assertThat(RwNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull(); - assertThat(RwNoFeatures.getCompileTimeAvailableFeatures()).isEmpty(); + assertThat(RwNoFeatures.getReadOnlySystemEnabledFeatures()).isEmpty(); } @Test @@ -72,7 +72,7 @@ public class SystemFeaturesGeneratorTest { assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull(); assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull(); assertThat(RoNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull(); - assertThat(RoNoFeatures.getCompileTimeAvailableFeatures()).isEmpty(); + assertThat(RoNoFeatures.getReadOnlySystemEnabledFeatures()).isEmpty(); // Also ensure we fall back to the PackageManager for feature APIs without an accompanying // versioned feature definition. @@ -106,7 +106,7 @@ public class SystemFeaturesGeneratorTest { assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull(); assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull(); assertThat(RwFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull(); - assertThat(RwFeatures.getCompileTimeAvailableFeatures()).isEmpty(); + assertThat(RwFeatures.getReadOnlySystemEnabledFeatures()).isEmpty(); } @Test @@ -163,7 +163,7 @@ public class SystemFeaturesGeneratorTest { assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 100)).isNull(); assertThat(RoFeatures.maybeHasFeature("", 0)).isNull(); - Map<String, FeatureInfo> compiledFeatures = RoFeatures.getCompileTimeAvailableFeatures(); + Map<String, FeatureInfo> compiledFeatures = RoFeatures.getReadOnlySystemEnabledFeatures(); assertThat(compiledFeatures.keySet()) .containsExactly(PackageManager.FEATURE_WATCH, PackageManager.FEATURE_WIFI); assertThat(compiledFeatures.get(PackageManager.FEATURE_WATCH).version).isEqualTo(1); |